Skip to content

Commit

Permalink
Cycles Renderer : Support batch rendering to ieDisplay
Browse files Browse the repository at this point in the history
This still doesn't support a mix of `ieDisplay` and file outputs, but that isn't a particularly common configuration at least. The output handling could benefit from a much bigger overhaul but here I've limited myself to the most minimal changes necessary to fix the problem. Future improvements could include :

- Moving more logic in CyclesOutput, instead of having much of it in `updateOutputs()`.
- A single `ccl::OutputDriver` subclass that supports writing to file via OIIO as well as rendering to `ieDisplay`.

That can wait though.

With this fix we can now use Batch renders where we logically want to in the unit tests instead of using Interactive renders just to use the display driver we want. I'm also pretty sure this fixes GafferHQ#5905, but can't say with 100% certainty because the issue description is so vague.
  • Loading branch information
johnhaddon committed Jun 17, 2024
1 parent 6dd06bb commit 10547c8
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 94 deletions.
3 changes: 3 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
1.4.x.x (relative to 1.4.7.0)
=======

Fixes
-----

- Cycles : Fixed rendering to the Catalogue using the batch Render node (#5905). Note that rendering a mixture of Catalogue and file outputs is still not supported, and in this case any file outputs will be ignored.

1.4.7.0 (relative to 1.4.6.0)
=======
Expand Down
101 changes: 17 additions & 84 deletions python/GafferCyclesTest/IECoreCyclesPreviewTest/RendererTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,7 @@ class RendererTest( GafferTest.TestCase ) :

def testObjectColor( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.output(
"testOutput",
Expand Down Expand Up @@ -95,7 +92,6 @@ def testObjectColor( self ) :
plane.transform( imath.M44f().translate( imath.V3f( 0, 0, 1 ) ) )

renderer.render()
time.sleep( 2 )

image = IECoreImage.ImageDisplayDriver.storedImage( "testObjectColor" )
self.assertIsInstance( image, IECoreImage.ImagePrimitive )
Expand All @@ -110,10 +106,7 @@ def testObjectColor( self ) :

def testQuadLightColorTexture( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.output(
"testOutput",
Expand Down Expand Up @@ -168,7 +161,6 @@ def testQuadLightColorTexture( self ) :
light.transform( imath.M44f().rotate( imath.V3f( 0, math.pi, 0 ) ) )

renderer.render()
time.sleep( 2.0 )

# Check that we have a pure red image.

Expand Down Expand Up @@ -208,10 +200,7 @@ def testLightWithoutAttribute( self ) :

def testBackgroundLightWithoutTexture( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.output(
"testOutput",
Expand Down Expand Up @@ -263,7 +252,6 @@ def testBackgroundLightWithoutTexture( self ) :
light.transform( imath.M44f().rotate( imath.V3f( 0, math.pi, 0 ) ) )

renderer.render()
time.sleep( 2.0 )

# Check that we have a pure red image.

Expand All @@ -279,10 +267,7 @@ def testBackgroundLightWithoutTexture( self ) :

def testCrashWhenNoBackgroundLight( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.option( "cycles:shadingsystem", IECore.StringData( "SVM" ) )

Expand Down Expand Up @@ -601,10 +586,7 @@ def backgroundShader( color ) :

def testMultipleOutputs( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.output(
"testOutput:beauty",
Expand Down Expand Up @@ -727,10 +709,7 @@ def testCameraAttributeEdits( self ) :

def testDisplayDriverCropWindow( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.camera(
"testCamera",
Expand Down Expand Up @@ -759,11 +738,6 @@ def testDisplayDriverCropWindow( self ) :

renderer.option( "camera", IECore.StringData( "testCamera" ) )
renderer.render()
## \todo We could just be running this test with a Batch mode render,
# in which case `render()` would block until the image was complete.
# But CyclesRenderer is currently hardcoded to only use IEDisplayOutputDriver
# for interactive renders.
time.sleep( 2.0 )
del renderer

image = IECoreImage.ImageDisplayDriver.storedImage( "testCropWindow" )
Expand Down Expand Up @@ -808,10 +782,7 @@ def testOutputFileCropWindow( self ) :

def testPointsWithNormals( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.output(
"pointsWithNormals",
Expand Down Expand Up @@ -861,7 +832,6 @@ def testPointsWithNormals( self ) :
pointsObject.transform( imath.M44f().translate( imath.V3f( 0, 0, 2 ) ) )

renderer.render()
time.sleep( 2 )

del pointsObject
del renderer
Expand All @@ -878,10 +848,7 @@ def testPointsWithNormals( self ) :

def __testMeshSmoothing( self, cube, smoothingExpected ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.output(
"meshSmoothing",
Expand All @@ -903,7 +870,6 @@ def __testMeshSmoothing( self, cube, smoothingExpected ) :
cubeObject.transform( imath.M44f().translate( imath.V3f( 0, 0, 2 ) ).rotate( imath.V3f( 0, math.pi / 4.0, 0 ) ) )

renderer.render()
time.sleep( 2 )

del cubeObject
del renderer
Expand Down Expand Up @@ -1623,10 +1589,7 @@ def testCustomAttributes( self ) :

def testCustomAttributePrecedence( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.output(
"testOutput",
Expand Down Expand Up @@ -1665,7 +1628,6 @@ def testCustomAttributePrecedence( self ) :
plane.transform( imath.M44f().translate( imath.V3f( 0, 0, 1 ) ) )

renderer.render()
time.sleep( 2 )

image = IECoreImage.ImageDisplayDriver.storedImage( "testCustomAttributePrecedence" )
self.assertIsInstance( image, IECoreImage.ImagePrimitive )
Expand Down Expand Up @@ -1742,10 +1704,7 @@ def testMissingShaderParameter( self ) :

def testOSLComponentConnections( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.output(
"testOutput",
Expand Down Expand Up @@ -1784,7 +1743,6 @@ def testOSLComponentConnections( self ) :
plane.transform( imath.M44f().translate( imath.V3f( 0, 0, 1 ) ) )

renderer.render()
time.sleep( 2 )

image = IECoreImage.ImageDisplayDriver.storedImage( "testOSLComponentConnections" )
self.assertIsInstance( image, IECoreImage.ImagePrimitive )
Expand All @@ -1794,10 +1752,7 @@ def testOSLComponentConnections( self ) :

def testSurfaceAttributeWithGenericShaderType( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.output(
"testOutput",
Expand Down Expand Up @@ -1834,7 +1789,6 @@ def testSurfaceAttributeWithGenericShaderType( self ) :
plane.transform( imath.M44f().translate( imath.V3f( 0, 0, 1 ) ) )

renderer.render()
time.sleep( 2 )

image = IECoreImage.ImageDisplayDriver.storedImage( "testSurfaceAttributeWithGenericShaderType" )
self.assertIsInstance( image, IECoreImage.ImagePrimitive )
Expand All @@ -1859,10 +1813,7 @@ def __valueAtUV( self, image, uv, channelName ) :

def testCustomAOV( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

# Custom AOVs are currently not supported in OSL mode.
# See https://developer.blender.org/T73266 for further updates
Expand Down Expand Up @@ -1934,7 +1885,6 @@ def testCustomAOV( self ) :
plane.transform( imath.M44f().translate( imath.V3f( 0, 0, 1 ) ) )

renderer.render()
time.sleep( 2 )

image = IECoreImage.ImageDisplayDriver.storedImage( "testCustomValueAOV" )
self.assertIsInstance( image, IECoreImage.ImagePrimitive )
Expand Down Expand Up @@ -2216,10 +2166,7 @@ def testUnsupportedShaderParameters( self ) :

def testUSDLightColorTemperature( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.output(
"testOutput",
Expand Down Expand Up @@ -2270,7 +2217,6 @@ def testUSDLightColorTemperature( self ) :
light.transform( imath.M44f().rotate( imath.V3f( 0, math.pi, 0 ) ) )

renderer.render()
time.sleep( 2.0 )

# Check that the color temperature has tinted the image red.

Expand All @@ -2288,10 +2234,7 @@ def testUSDLightColorTemperature( self ) :

def testOSLInSVMShadingSystem( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.option( "cycles:shadingsystem", IECore.StringData( "SVM" ) )

Expand Down Expand Up @@ -2336,7 +2279,6 @@ def testOSLInSVMShadingSystem( self ) :
plane.transform( imath.M44f().translate( imath.V3f( 0, 0, 1 ) ) )

renderer.render()
time.sleep( 2 )

image = IECoreImage.ImageDisplayDriver.storedImage( "testOSLInSVMShadingSystem" )
self.assertIsInstance( image, IECoreImage.ImagePrimitive )
Expand All @@ -2352,10 +2294,7 @@ def testOSLInSVMShadingSystem( self ) :

def testFilmOptions( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

# Get default values

Expand Down Expand Up @@ -2393,10 +2332,7 @@ def testFilmOptions( self ) :

def testIntegratorOptions( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

# Get default values

Expand Down Expand Up @@ -2434,10 +2370,7 @@ def testIntegratorOptions( self ) :

def testUnknownOptions( self ) :

renderer = GafferScene.Private.IECoreScenePreview.Renderer.create(
"Cycles",
GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Interactive,
)
renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( "Cycles" )

renderer.output(
"testOutput",
Expand Down
28 changes: 18 additions & 10 deletions src/GafferCycles/IECoreCyclesPreview/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,16 +283,13 @@ class CyclesOutput : public IECore::RefCounted
public :

CyclesOutput( const IECore::InternedString &name, const IECoreScene::Output *output )
: m_passType( ccl::PASS_NONE ), m_denoise( false ), m_interactive( false ), m_lightgroup( false )
: m_passType( ccl::PASS_NONE ), m_denoise( false ), m_useIEDisplay( output->getType() == "ieDisplay" ), m_lightgroup( false )
{
m_parameters = output->parametersData()->copy();
CompoundDataMap &p = m_parameters->writable();

p["path"] = new StringData( output->getName() );

if( output->getType() == "ieDisplay" )
m_interactive = true;

m_denoise = parameter<bool>( output->parameters(), "denoise", false );

const ccl::NodeEnum &typeEnum = *ccl::Pass::get_type_enum();
Expand Down Expand Up @@ -373,7 +370,7 @@ class CyclesOutput : public IECore::RefCounted
ccl::PassType m_passType;
std::string m_data;
bool m_denoise;
bool m_interactive;
bool m_useIEDisplay;
bool m_lightgroup;
};

Expand Down Expand Up @@ -3123,11 +3120,18 @@ class CyclesRenderer final : public IECoreScenePreview::Renderer
InternedString cryptoMaterial;
bool hasShadowCatcher = false;
bool hasDenoise = false;
const bool useIEDisplay = std::any_of(
m_outputs.begin(), m_outputs.end(),
[] ( const auto &output ) { return output.second->m_useIEDisplay; }
);
for( auto &coutput : m_outputs )
{
if( ( m_renderType != Interactive && coutput.second->m_interactive ) ||
( m_renderType == Interactive && !coutput.second->m_interactive ) )
if( coutput.second->m_useIEDisplay != useIEDisplay )
{
/// \todo Support a mix of IEDisplay and file outputs. To do
/// this we'd make a single `ccl::OutputDriver` subclass
/// that could cope with both types.
IECore::msg( IECore::Msg::Warning, "CyclesRenderer", fmt::format( "Ignoring output \"{}\"", coutput.first.string() ) );
continue;
}

Expand Down Expand Up @@ -3238,9 +3242,9 @@ class CyclesRenderer final : public IECoreScenePreview::Renderer
// Add lightgroups on the end
for( auto &coutput : m_outputs )
{
if( ( m_renderType != Interactive && coutput.second->m_interactive ) ||
( m_renderType == Interactive && !coutput.second->m_interactive ) )
if( coutput.second->m_useIEDisplay != useIEDisplay )
{
IECore::msg( IECore::Msg::Warning, "CyclesRenderer", fmt::format( "Ignoring output \"{}\"", coutput.first.string() ) );
continue;
}

Expand Down Expand Up @@ -3276,10 +3280,14 @@ class CyclesRenderer final : public IECoreScenePreview::Renderer
film->set_use_approximate_shadow_catcher( !hasShadowCatcher );
m_scene->integrator->set_use_denoise( hasDenoise );

if( m_renderType == Interactive )
if( useIEDisplay )
{
m_session->set_output_driver( ccl::make_unique<IEDisplayOutputDriver>( displayWindow, dataWindow, layersData->readable() ) );
}
else
{
m_session->set_output_driver( ccl::make_unique<OIIOOutputDriver>( displayWindow, dataWindow, layersData->readable() ) );
}

m_outputsChanged = false;
}
Expand Down

0 comments on commit 10547c8

Please sign in to comment.