Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 141 additions & 8 deletions source/MaterialXRenderOsl/OslRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,132 @@ void OslRenderer::renderOSL(const FilePath& dirPath, const string& shaderName, c
}
}

void OslRenderer::renderOSLNodes(const FilePath& dirPath, const string& shaderName, const string& outputName)
{
// If command options missing, skip testing.
if (_oslTestRenderExecutable.isEmpty() ||
_oslTestRenderSceneTemplateFile.isEmpty() || _oslUtilityOSOPath.isEmpty())
{
throw ExceptionRenderError("Command input arguments are missing");
}

// Determine the shader path from output path and shader name
FilePath shaderFilePath(dirPath);
shaderFilePath = shaderFilePath / shaderName;
string shaderPath = shaderFilePath.asString();

// Set output image name.
string outputFileName = shaderPath + "_oslcmd.png";
_oslOutputFileName = outputFileName;

// Use a known error file name to check
string errorFile(shaderPath + "_render_errors_oslcmd.txt");
const string redirectString(" 2>&1");

// Read in scene template and replace the applicable tokens to have a valid ShaderGroup.
// Write to local file to use as input for rendering.
std::ifstream sceneTemplateStream(_oslTestRenderSceneTemplateFile);
string sceneTemplateString;
sceneTemplateString.assign(std::istreambuf_iterator<char>(sceneTemplateStream),
std::istreambuf_iterator<char>());

// Perform token replacement
const string ENVIRONMENT_SHADER_PARAMETER_OVERRIDES("%environment_shader_parameter_overrides%");
const string BACKGROUND_COLOR_STRING("%background_color%");
const string OSL_COMMANDS("%oslCmd%");

StringMap replacementMap;

string envOverrideString;
for (const auto& param : _envOslShaderParameterOverrides)
{
envOverrideString.append(param);
}
replacementMap[ENVIRONMENT_SHADER_PARAMETER_OVERRIDES] = envOverrideString;
replacementMap[OSL_COMMANDS] = _oslCmdStr;
replacementMap[BACKGROUND_COLOR_STRING] = std::to_string(DEFAULT_SCREEN_COLOR_LIN_REC709[0]) + " " +
std::to_string(DEFAULT_SCREEN_COLOR_LIN_REC709[1]) + " " +
std::to_string(DEFAULT_SCREEN_COLOR_LIN_REC709[2]);
string sceneString = replaceSubstrings(sceneTemplateString, replacementMap);
if ((sceneString == sceneTemplateString) || sceneTemplateString.empty())
{
throw ExceptionRenderError("Scene template file: " + _oslTestRenderSceneTemplateFile.asString() +
" does not include proper tokens for rendering");
}

// Set the working directory for rendering.
FileSearchPath searchPath = getDefaultDataSearchPath();
FilePath rootPath = searchPath.isEmpty() ? FilePath() : searchPath[0];
FilePath origWorkingPath = FilePath::getCurrentPath();
rootPath.setCurrentPath();

// Write scene file
const string sceneFileName("scene_template_oslcmd.xml");
std::ofstream shaderFileStream;
shaderFileStream.open(sceneFileName);

if (shaderFileStream.is_open())
{
shaderFileStream << sceneString;
shaderFileStream.close();
}

// Set oso file paths
string osoPaths(_oslUtilityOSOPath);
osoPaths += PATH_LIST_SEPARATOR + dirPath.asString();
osoPaths += PATH_LIST_SEPARATOR + dirPath.getParentPath().asString();

// Build and run render command
string command(_oslTestRenderExecutable);
command += " " + sceneFileName;
command += " " + outputFileName;
command += " -r " + std::to_string(_width) + " " + std::to_string(_height);
command += " --path " + osoPaths;
command += " -aa " + std::to_string(_raysPerPixelLit);
command += " > " + errorFile + redirectString;

// Repeat the render command to allow for sporadic errors.
int returnValue = 0;
for (int i = 0; i < 5; i++)
{
returnValue = std::system(command.c_str());
if (!returnValue)
{
break;
}
}

// Restore the working directory after rendering.
origWorkingPath.setCurrentPath();

// Report errors on a non-zero return value.
if (returnValue)
{
std::ifstream errorStream(errorFile);
StringVec result;
string line;
unsigned int errCount = 0;
while (std::getline(errorStream, line))
{
if (errCount++ > 10)
{
break;
}
result.push_back(line);
}

StringVec errors;
errors.push_back("Errors reported in renderOSL:");
for (size_t i = 0; i < result.size(); i++)
{
errors.push_back(result[i]);
}
errors.push_back("Command string: " + command);
errors.push_back("Command return code: " + std::to_string(returnValue));
throw ExceptionRenderError("OSL rendering error", errors);
}
}

void OslRenderer::shadeOSL(const FilePath& dirPath, const string& shaderName, const string& outputName)
{
// If no command and include path specified then skip checking.
Expand Down Expand Up @@ -381,20 +507,27 @@ void OslRenderer::render()

_oslOutputFileName.assign(EMPTY_STRING);

// Use testshade
if (!_useTestRender)
if (_useOSLCmdStr)
{
shadeOSL(_oslOutputFilePath, _oslShaderName, _oslShaderOutputName);
renderOSLNodes(_oslOutputFilePath, _oslShaderName, _oslShaderOutputName);
}

// Use testrender
else
{
if (_oslShaderName.empty())
// Use testshade
if (!_useTestRender)
{
throw ExceptionRenderError("OSL shader name has not been specified");
shadeOSL(_oslOutputFilePath, _oslShaderName, _oslShaderOutputName);
}

// Use testrender
else
{
if (_oslShaderName.empty())
{
throw ExceptionRenderError("OSL shader name has not been specified");
}
renderOSL(_oslOutputFilePath, _oslShaderName, _oslShaderOutputName);
}
renderOSL(_oslOutputFilePath, _oslShaderName, _oslShaderOutputName);
}
}

Expand Down
19 changes: 19 additions & 0 deletions source/MaterialXRenderOsl/OslRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ class MX_RENDEROSL_API OslRenderer : public ShaderRenderer
_useTestRender = useTestRender;
}

/// Used to switch between testing oso files and osl command strings
void useOslCommandString(bool useOslCmdstr)
{
_useOSLCmdStr = useOslCmdstr;
}

/// Set the number of rays per pixel to be used for lit surfaces.
void setRaysPerPixelLit(int rays)
{
Expand All @@ -208,6 +214,11 @@ class MX_RENDEROSL_API OslRenderer : public ShaderRenderer
_raysPerPixelUnlit = rays;
}

/// Set the osl command string that is to be tested
void setOSLCmdStr(const string& oslCmd)
{
_oslCmdStr = oslCmd;
}
///
/// Compile OSL code stored in a file. Will throw an exception if an error occurs.
/// @param oslFilePath OSL file path.
Expand All @@ -230,6 +241,12 @@ class MX_RENDEROSL_API OslRenderer : public ShaderRenderer
/// @param outputName Name of OSL shader output to use.
void renderOSL(const FilePath& dirPath, const string& shaderName, const string& outputName);

/// Render using OSL command string. Will throw an exception if an error occurs.
/// @param dirPath Path to location containing input .oso file.
/// @param shaderName Name of OSL shader. A corresponding .oso file is assumed to exist in the output path folder.
/// @param outputName Name of OSL shader output to use.
void renderOSLNodes(const FilePath& dirPath, const string& shaderName, const string& outputName);

/// Constructor
OslRenderer(unsigned int width, unsigned int height, Image::BaseType baseType);

Expand All @@ -249,8 +266,10 @@ class MX_RENDEROSL_API OslRenderer : public ShaderRenderer
string _oslShaderOutputType;
FilePath _oslUtilityOSOPath;
bool _useTestRender;
bool _useOSLCmdStr;
int _raysPerPixelLit;
int _raysPerPixelUnlit;
string _oslCmdStr;
};

MATERIALX_NAMESPACE_END
Expand Down
49 changes: 40 additions & 9 deletions source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class BitangentOsl : public mx::ShaderNodeImpl
class OslShaderRenderTester : public RenderUtil::ShaderRenderTester
{
public:
explicit OslShaderRenderTester(mx::ShaderGeneratorPtr shaderGenerator) :
explicit OslShaderRenderTester(mx::ShaderGeneratorPtr shaderGenerator, bool useOslCmdStr) :
RenderUtil::ShaderRenderTester(shaderGenerator)
{
// Preprocess to resolve to absolute image file names
Expand All @@ -85,7 +85,7 @@ class OslShaderRenderTester : public RenderUtil::ShaderRenderTester
_customFilenameResolver = mx::StringResolver::create();
_customFilenameResolver->setFilenameSubstitution("\\\\", "/");
_customFilenameResolver->setFilenameSubstitution("\\", "/");

_useOslCmdStr = useOslCmdStr;
}

protected:
Expand Down Expand Up @@ -115,6 +115,7 @@ class OslShaderRenderTester : public RenderUtil::ShaderRenderTester

mx::ImageLoaderPtr _imageLoader;
mx::OslRendererPtr _renderer;
bool _useOslCmdStr;
};

// Renderer setup
Expand Down Expand Up @@ -235,6 +236,13 @@ bool OslShaderRenderTester::runRenderer(const std::string& shaderName,
return false;
}
CHECK(shader->getSourceCode().length() > 0);

std::string oslCmdStr = shader->getSourceCode();
/// TODO: this is a temp value.
oslCmdStr = "shader test_node test_node;\n";
oslCmdStr += "shader closure_passthrough closure_passthrough;\n";
oslCmdStr += "connect test_node.Out_Ci closure_passthrough.Cin;\n";


std::string shaderPath;
mx::FilePath outputFilePath = outputPath;
Expand All @@ -257,7 +265,7 @@ bool OslShaderRenderTester::runRenderer(const std::string& shaderName,
{
mx::ScopedTimer ioTimer(&profileTimes.languageTimes.ioTime);
std::ofstream file;
file.open(shaderPath + ".osl");
file.open(shaderPath + (_useOslCmdStr ? ".oslcmd" : ".osl"));
file << shader->getSourceCode();
file.close();
}
Expand All @@ -273,10 +281,11 @@ bool OslShaderRenderTester::runRenderer(const std::string& shaderName,
_renderer->setRaysPerPixelUnlit(testOptions.enableReferenceQuality ? 8 : 1);

// Validate compilation
{
mx::ScopedTimer compileTimer(&profileTimes.languageTimes.compileTime);
_renderer->createProgram(shader);
}
if (!_useOslCmdStr)
{
mx::ScopedTimer compileTimer(&profileTimes.languageTimes.compileTime);
_renderer->createProgram(shader);
}

_renderer->setSize(static_cast<unsigned int>(testOptions.renderSize[0]), static_cast<unsigned int>(testOptions.renderSize[1]));

Expand All @@ -299,10 +308,15 @@ bool OslShaderRenderTester::runRenderer(const std::string& shaderName,

const std::string& outputName = output->getVariable();
const std::string& outputType = typeSyntax.getTypeAlias().empty() ? typeSyntax.getName() : typeSyntax.getTypeAlias();
const std::string& sceneTemplateFile = "scene_template.xml";
const std::string& sceneTemplateFile = (_useOslCmdStr ? "scene_template_oslcmd.xml": "scene_template.xml");

// Set shader output name and type to use
_renderer->setOslShaderOutput(outputName, outputType);

// Get oslcmdstr
_renderer->setOSLCmdStr(oslCmdStr);
_renderer->useOslCommandString(_useOslCmdStr);


// Set scene template file. For now we only have the constant color scene file
mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath();
Expand Down Expand Up @@ -365,6 +379,23 @@ TEST_CASE("Render: OSL TestSuite", "[renderosl]")
mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath();
mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx");

OslShaderRenderTester renderTester(mx::OslShaderGenerator::create());
OslShaderRenderTester renderTester(mx::OslShaderGenerator::create(), false);
renderTester.validate(optionsFilePath);
}

TEST_CASE("Render: OSL Nodes TestSuite", "[oslNodes]")
{
if (std::string(MATERIALX_OSL_BINARY_OSLC).empty() &&
std::string(MATERIALX_OSL_BINARY_TESTRENDER).empty())
{
INFO("Skipping the OSL test suite as its executable locations haven't been set.");
return;
}

mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath();
mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx");

/// TODO: change to chris' new shader generator
OslShaderRenderTester renderTester(mx::OslShaderGenerator::create(), true);
renderTester.validate(optionsFilePath);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!-- Template for a lit scene in testrender -->
<World>
<Camera eye="0, 0, 3" dir="0, 0, -1" fov="79.334" />

<!-- Background environment map. -->
<ShaderGroup>
%environment_shader_parameter_overrides%
shader envmap layer1;
</ShaderGroup>
<Background resolution="2048" />

<!-- Background quad with raytype test. -->
<ShaderGroup>
color Cin %background_color%;
%environment_shader_parameter_overrides%
shader raytype_background layer1;
</ShaderGroup>
<Quad corner="-340, -340, -800" edge_x="680, 0, 0" edge_y="0, 680, 0" />

<!-- Shader graph for routing to output layer:
input_shader_parameter_overrides : are parameter overrides for the input shader
%input_shader_type% : type of an input shader to feed into the output shader.
%input_shader_output% : name of output argument on input shader.
%output_shader_type%: type of the output shader used to render with
%output_shader_input% : name of input argument on output shader.
-->
<ShaderGroup>
%oslCmd%
</ShaderGroup>

<Sphere center="0, 0, 0" radius="1" />
</World>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
surface

test_node(
color test_color = color(1, 0, 0),
output closure color Out_Ci = 0
){
Out_Ci = test_color * diffuse(N);
}