Skip to content

Commit

Permalink
Wedge rendering unit tests (#969)
Browse files Browse the repository at this point in the history
* Initial block of code.

* Fix up wedge image vec return.

* Unit test setup changes for GLSL and OSL (test render) wedging.

* Remove accidental JSON test change.

* Env consistency:
- Turn off direct lighting for GLSL by default.
- Add background quad for OSL rendering to hide environment.
- Fix flip in OSL save.

* Review fixes.
  • Loading branch information
bernardkwok authored Sep 30, 2020
1 parent 6e91ddf commit 2fde5d0
Show file tree
Hide file tree
Showing 21 changed files with 289 additions and 52 deletions.
19 changes: 9 additions & 10 deletions resources/Materials/TestSuite/Utilities/closure_color_scene.xml
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
<World>
<Camera eye="0, 0, 4" dir="0,0,-1" fov="70" />

<!-- Background clear. Uncomment this use flat color
%background_color% : is the background color
-->
<!--
<!-- Background environment map. -->
<ShaderGroup>
color Cin %background_color%;
shader constant_color_background layer1;
%environment_shader_parameter_overrides%
shader envmap layer1;
</ShaderGroup>
<Background resolution="1024" />

<!-- Background quad.
%background_color% : is the background color
-->
<!-- Background environment map. Comment this out to use flat color -->
<ShaderGroup>
%environment_shader_parameter_overrides%
shader envmap layer1;
color Cin %background_color%;
shader constant_color layer1;
</ShaderGroup>
<Background resolution="1024" />
<Quad corner="-400,-400,-800" edge_x="800,0,0" edge_y="0,800,0" />

<!-- Shader graph for routing to output layer:
input_shader_parameter_overrides : are parameter overrides for the input shader
Expand Down
9 changes: 8 additions & 1 deletion resources/Materials/TestSuite/_options.mtlx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<parameter name="geometryScale" type="float" value="40.0" />

<!-- Enable direct lighting. Default is true. -->
<parameter name="enableDirectLighting" type="boolean" value="true" />
<parameter name="enableDirectLighting" type="boolean" value="false" />

<!-- Enable indirect lighting. Default is true. -->
<parameter name="enableIndirectLighting" type="boolean" value="true" />
Expand Down Expand Up @@ -97,6 +97,13 @@

<!-- Test prototype functionality for future versions of MaterialX -->
<parameter name="applyFutureUpdates" type="boolean" value="true" />

<!-- Wedge rendering options -->
<parameter name="wedgeFiles" type="string" value="conductor.mtlx" />
<parameter name="wedgeParameters" type="string" value="test_conductor/roughness1/roughness" />
<parameter name="wedgeRangeMin" type="floatarray" value="0.0" />
<parameter name="wedgeRangeMax" type="floatarray" value="1.0" />
<parameter name="wedgeSteps" type="integerarray" value="8" />

</nodedef>
</materialx>
5 changes: 3 additions & 2 deletions source/MaterialXRender/Image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ ImagePtr createUniformImage(unsigned int width, unsigned int height, unsigned in

ImagePtr createImageStrip(const vector<ImagePtr>& imageVec)
{
if (imageVec.empty())
if (imageVec.empty() || !imageVec[0])
{
return nullptr;
}
Expand All @@ -53,7 +53,8 @@ ImagePtr createImageStrip(const vector<ImagePtr>& imageVec)
unsigned int xOffset = 0;
for (ImagePtr srcImage : imageVec)
{
if (srcImage->getWidth() != srcWidth ||
if (!srcImage ||
srcImage->getWidth() != srcWidth ||
srcImage->getHeight() != srcHeight ||
srcImage->getChannelCount() != channelCount ||
srcImage->getBaseType() != baseType)
Expand Down
3 changes: 3 additions & 0 deletions source/MaterialXRender/Image.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ using ConstImagePtr = shared_ptr<const Image>;
/// A map from strings to images.
using ImageMap = std::unordered_map<string, ImagePtr>;

/// A vetor of images.
using ImageVec = std::vector<ImagePtr>;

/// A pair of images.
using ImagePair = std::pair<ImagePtr, ImagePtr>;

Expand Down
2 changes: 1 addition & 1 deletion source/MaterialXRender/ShaderRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class ShaderRenderer

/// Save the current contents of the off-screen hardware buffer to disk.
/// @param filePath Path to file to save rendered image to.
virtual void saveImage(const FilePath& filePath) = 0;
virtual void saveImage(const FilePath& filePath, ConstImagePtr image, bool verticalFlip) = 0;

/// @}

Expand Down
5 changes: 2 additions & 3 deletions source/MaterialXRenderGlsl/GlslRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,13 +340,12 @@ ImagePtr GlslRenderer::captureImage()
return result;
}

void GlslRenderer::saveImage(const FilePath& filePath)
void GlslRenderer::saveImage(const FilePath& filePath, ConstImagePtr image, bool verticalFlip)
{
StringVec errors;
const string errorType("GLSL image save error.");

ImagePtr image = captureImage();
if (!_imageHandler->saveImage(filePath, image, true))
if (!_imageHandler->saveImage(filePath, image, verticalFlip))
{
errors.push_back("Failed to save to file:" + filePath.asString());
throw ExceptionShaderRenderError(errorType, errors);
Expand Down
2 changes: 1 addition & 1 deletion source/MaterialXRenderGlsl/GlslRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class GlslRenderer : public ShaderRenderer

/// Save the current contents of the off-screen hardware buffer to disk.
/// @param filePath Name of file to save rendered image to.
void saveImage(const FilePath& filePath) override;
void saveImage(const FilePath& filePath, ConstImagePtr image, bool verticalFlip) override;

/// Return the GL frame buffer.
GLFrameBufferPtr getFrameBuffer() const
Expand Down
39 changes: 35 additions & 4 deletions source/MaterialXRenderOsl/OslRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ void OslRenderer::renderOSL(const FilePath& dirPath, const string& shaderName, c

// Set output image name.
string outputFileName = shaderPath + "_osl.png";
// Cache the output file name
_oslOutputFileName = outputFileName;

// Use a known error file name to check
string errorFile(shaderPath + "_render_errors.txt");
Expand Down Expand Up @@ -225,6 +227,8 @@ void OslRenderer::shadeOSL(const FilePath& dirPath, const string& shaderName, co

// Set output image name.
string outputFileName = shaderPath + ".testshade.png";
// Cache the output file name
_oslOutputFileName = outputFileName;

// Use a known error file name to check
string errorFile(shaderPath + "_shade_errors.txt");
Expand Down Expand Up @@ -390,6 +394,8 @@ void OslRenderer::render()
throw ExceptionShaderRenderError(errorType, errors);
}

_oslOutputFileName.assign(EMPTY_STRING);

// Use testshade
if (!_useTestRender)
{
Expand All @@ -410,13 +416,38 @@ void OslRenderer::render()

ImagePtr OslRenderer::captureImage()
{
// No-op: image capture is done as part of rendering.
return nullptr;
// As rendering goes to disk need to read the image back from disk
StringVec errors;
const string errorType("OSL image save error.");

if (!_imageHandler || _oslOutputFileName.isEmpty())
{
errors.push_back("Failed to read image: " + _oslOutputFileName.asString());
throw ExceptionShaderRenderError(errorType, errors);
}

string error;
ImagePtr returnImage = _imageHandler->acquireImage(_oslOutputFileName, false, nullptr, &error);
if (!returnImage)
{
errors.push_back("Failed to save to file: " + _oslOutputFileName.asString());
errors.push_back(error);
throw ExceptionShaderRenderError(errorType, errors);
}

return returnImage;
}

void OslRenderer::saveImage(const FilePath& /*filePath*/)
void OslRenderer::saveImage(const FilePath& filePath, ConstImagePtr image, bool verticalFlip)
{
// No-op: image save is done as part of rendering.
StringVec errors;
const string errorType("GLSL image save error.");

if (!_imageHandler->saveImage(filePath, image, verticalFlip))
{
errors.push_back("Failed to save to file: " + filePath.asString());
throw ExceptionShaderRenderError(errorType, errors);
}
}

} // namespace MaterialX
4 changes: 3 additions & 1 deletion source/MaterialXRenderOsl/OslRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class OslRenderer : public ShaderRenderer
/// does not perform any action as render() produces images as part if it's
/// execution.
/// @param filePath Name of file to save rendered image to.
void saveImage(const FilePath& filePath) override;
void saveImage(const FilePath& filePath, ConstImagePtr image, bool verticalFlip) override;

/// @}
/// @name Compilation settings
Expand Down Expand Up @@ -235,6 +235,8 @@ class OslRenderer : public ShaderRenderer
FilePath _oslIncludePath;
/// Output file path. File name does not include an extension
FilePath _oslOutputFilePath;
/// Output image file name
FilePath _oslOutputFileName;

/// Path to "testshade" executable
FilePath _oslTestShadeExecutable;
Expand Down
7 changes: 0 additions & 7 deletions source/MaterialXTest/MaterialXGenMdl/GenMdl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,6 @@ void MdlShaderGeneratorTester::compileSource(const std::vector<mx::FilePath>& so
}
else
{
// Make this a build option
//std::string renderExec("D:/Work/materialx/df_cuda_rel_325000.1814/df_cuda.exe");
//if (renderExec.empty())
//{
// return;
//}

std::string renderCommand = renderExec;
for (const std::string& extraPath : extraModulePaths)
{
Expand Down
26 changes: 26 additions & 0 deletions source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,11 @@ bool TestSuiteOptions::readOptions(const std::string& optionFile)
const std::string EXTERNAL_LIBRARY_PATHS("externalLibraryPaths");
const std::string EXTERNAL_TEST_PATHS("externalTestPaths");
const std::string APPLY_LATEST_UPDATES("applyFutureUpdates");
const std::string WEDGE_FILES("wedgeFiles");
const std::string WEDGE_PARAMETERS("wedgeParameters");
const std::string WEDGE_RANGE_MIN("wedgeRangeMin");
const std::string WEDGE_RANGE_MAX("wedgeRangeMax");
const std::string WEDGE_STEPS("wedgeSteps");

overrideFiles.clear();
dumpGeneratedCode = false;
Expand Down Expand Up @@ -1106,6 +1111,27 @@ bool TestSuiteOptions::readOptions(const std::string& optionFile)
externalTestPaths.append(mx::FilePath(l));
}
}

else if (name == WEDGE_FILES)
{
wedgeFiles = mx::splitString(p->getValueString(), ",");
}
else if (name == WEDGE_PARAMETERS)
{
wedgeParameters = mx::splitString(p->getValueString(), ",");
}
else if (name == WEDGE_STEPS)
{
wedgeSteps = val->asA<mx::IntVec>();
}
else if (name == WEDGE_RANGE_MIN)
{
wedgeRangeMin = val->asA<mx::FloatVec>();
}
else if (name == WEDGE_RANGE_MAX)
{
wedgeRangeMax = val->asA<mx::FloatVec>();
}
else if (name == APPLY_LATEST_UPDATES)
{
applyFutureUpdates = p->getValue()->asA<bool>();
Expand Down
7 changes: 7 additions & 0 deletions source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,13 @@ class TestSuiteOptions
// Additional testPaths paths
mx::FileSearchPath externalTestPaths;

// Wedge parameters
mx::StringVec wedgeFiles;
mx::StringVec wedgeParameters;
mx::FloatVec wedgeRangeMin;
mx::FloatVec wedgeRangeMax;
mx::IntVec wedgeSteps;

// Apply updates for future versions
bool applyFutureUpdates;
};
Expand Down
114 changes: 113 additions & 1 deletion source/MaterialXTest/MaterialXRender/RenderUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,119 @@ bool ShaderRenderTester::validate(const mx::FilePathVec& testRootPaths, const mx
mx::InterfaceElementPtr nodeGraphImpl = nodeGraph ? nodeGraph->getImplementation() : nullptr;
usedImpls.insert(nodeGraphImpl ? nodeGraphImpl->getName() : impl->getName());
}
runRenderer(elementName, targetElement, context, doc, log, options, profileTimes, imageSearchPath, outputPath);

const mx::StringVec& wedgeFiles = options.wedgeFiles;
const mx::StringVec& wedgeParameters = options.wedgeParameters;
const mx::FloatVec& wedgeRangeMin = options.wedgeRangeMin;
const mx::FloatVec& wedgeRangeMax = options.wedgeRangeMax;
const mx::IntVec& wedgeSteps = options.wedgeSteps;
bool performWedge = (!wedgeFiles.empty()) &&
wedgeFiles.size() == wedgeParameters.size() &&
wedgeFiles.size() == wedgeRangeMin.size() &&
wedgeFiles.size() == wedgeRangeMax.size() &&
wedgeFiles.size() == wedgeSteps.size();

if (!performWedge)
{
runRenderer(elementName, targetElement, context, doc, log, options, profileTimes, imageSearchPath, outputPath, nullptr);
}
else
{
mx::ImageVec imageVec;
for (size_t f = 0; f < wedgeFiles.size(); f++)
{
const std::string& wedgeFile = wedgeFiles[f];
if (wedgeFile != file)
{
continue;
}

// Make this a utility
std::string parameterPath = wedgeParameters[f];
mx::ElementPtr uniformElement = doc->getDescendant(parameterPath);
if (!uniformElement)
{
std::string nodePath = mx::parentNamePath(parameterPath);
mx::ElementPtr uniformParent = doc->getDescendant(nodePath);
if (uniformParent)
{
mx::NodePtr uniformNode = uniformParent->asA<mx::Node>();
if (uniformNode)
{
mx::StringVec pathVec = mx::splitNamePath(parameterPath);
uniformNode->addInputFromNodeDef(pathVec[pathVec.size() - 1]);
}
}
}
uniformElement = doc->getDescendant(parameterPath);
mx::ValueElementPtr valueElement = uniformElement ? uniformElement->asA<mx::ValueElement>() : nullptr;
if (!valueElement)
{
continue;
}

mx::ValuePtr origPropertyValue(valueElement ? valueElement->getValue() : nullptr);
mx::ValuePtr newValue = valueElement->getValue();

float wedgePropertyMin = wedgeRangeMin[f];
float wedgePropertyMax = wedgeRangeMax[f];
int wedgeImageCount = std::max(wedgeSteps[f], 2);

float wedgePropertyStep = (wedgePropertyMax - wedgePropertyMin) / (wedgeImageCount - 1);
for (int w = 0; w < wedgeImageCount; w++)
{
bool setValue = false;
float propertyValue = (w == wedgeImageCount - 1) ? wedgePropertyMax : wedgePropertyMin + wedgePropertyStep * w;
if (origPropertyValue->isA<int>())
{
valueElement->setValue(static_cast<int>(propertyValue));
setValue = true;
}
else if (origPropertyValue->isA<float>())
{
valueElement->setValue(propertyValue);
setValue = true;
}
else if (origPropertyValue->isA<mx::Vector2>())
{
mx::Vector2 val(propertyValue, propertyValue);
valueElement->setValue(val);
setValue = true;
}
else if (origPropertyValue->isA<mx::Color3>() ||
origPropertyValue->isA<mx::Vector3>())
{
mx::Vector3 val(propertyValue, propertyValue, propertyValue);
valueElement->setValue(val);
setValue = true;
}
else if (origPropertyValue->isA<mx::Color4>() ||
origPropertyValue->isA<mx::Vector4>())
{
mx::Vector4 val(propertyValue, propertyValue, propertyValue, origPropertyValue->isA<mx::Color4>() ? 1.0f : propertyValue);
valueElement->setValue(val);
setValue = true;
}

if (setValue)
{
runRenderer(elementName, targetElement, context, doc, log, options, profileTimes, imageSearchPath, outputPath, &imageVec);
}
}

if (!imageVec.empty())
{
mx::ImagePtr wedgeImage = mx::createImageStrip(imageVec);
if (wedgeImage)
{
std::string wedgeFileName = mx::createValidName(mx::replaceSubstrings(parameterPath, pathMap));
wedgeFileName += "_" + _languageTargetString + ".bmp";
mx::FilePath wedgePath = outputPath / wedgeFileName;
saveImage(wedgePath, wedgeImage, true);
}
}
}
}
}
}
}
Expand Down
Loading

0 comments on commit 2fde5d0

Please sign in to comment.