Skip to content

Commit

Permalink
Merge branch '1.3_maintenance' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
johnhaddon committed Nov 21, 2023
2 parents 582fe39 + d4c707a commit 0c79105
Show file tree
Hide file tree
Showing 19 changed files with 766 additions and 64 deletions.
25 changes: 25 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,32 @@ Breaking Changes
1.3.x.x (relative to 1.3.7.0)
=======

Features
--------

- Viewer : Added "Snapshot To Catalogue" command to the right-click menu of the 3D view.

Improvements
------------

- ImageTransform, Resample : Improved performance for non-separable filters without scaling, with 2-6x speedups in some benchmark cases.

Fixes
-----

- InteractiveRender : Fixed unnecessary updates to encapsulated locations when deforming an unrelated object.
- InteractiveArnoldRender : Fixed creation of new Catalogue image when editing output metadata or pixel filter.
- Windows `Scene/OpenGL/Shader` Menu : Removed `\` at the beginning of menu items.

API
---

- SceneGadget : Added `snapshotToFile()` method.

Build
-----

- Instancer : Fixed ambiguous reference compilation errors when building with Boost 1.70.

1.3.7.0 (relative to 1.3.6.1)
=======
Expand Down
2 changes: 1 addition & 1 deletion SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -1105,7 +1105,7 @@ libraries = {

"GafferSceneUI" : {
"envAppends" : {
"LIBS" : [ "Gaffer", "GafferUI", "GafferImage", "GafferImageUI", "GafferScene", "Iex$IMATH_LIB_SUFFIX", "IECoreGL$CORTEX_LIB_SUFFIX", "IECoreImage$CORTEX_LIB_SUFFIX", "IECoreScene$CORTEX_LIB_SUFFIX" ],
"LIBS" : [ "Gaffer", "GafferUI", "GafferImage", "GafferImageUI", "GafferScene", "Iex$IMATH_LIB_SUFFIX", "IECoreGL$CORTEX_LIB_SUFFIX", "IECoreImage$CORTEX_LIB_SUFFIX", "IECoreScene$CORTEX_LIB_SUFFIX", "OpenImageIO$OIIO_LIB_SUFFIX", "OpenImageIO_Util$OIIO_LIB_SUFFIX" ],
},
"pythonEnvAppends" : {
"LIBS" : [ "IECoreGL$CORTEX_LIB_SUFFIX", "GafferBindings", "GafferScene", "GafferImage", "GafferUI", "GafferImageUI", "GafferSceneUI", "IECoreScene$CORTEX_LIB_SUFFIX" ],
Expand Down
6 changes: 5 additions & 1 deletion config/ie/options
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ ocioVersion = targetAppReg.get( "OpenColorIOVersion", cortexReg["OpenColorIOVers
exrVersion = targetAppReg.get( "OpenEXRVersion", cortexReg["OpenEXRVersion"] )
oiioVersion = targetAppReg.get( "OpenImageIOVersion", cortexReg["OpenImageIOVersion"] )
oiioLibSuffix = targetAppReg.get( "OpenImageIOLibSuffix", oiioVersion )
ocioLibSuffix = targetAppReg.get( "OpenColorIOLibSuffix", IEEnv.BuildUtil.libSuffix( "OpenColorIO", ocioVersion ) )
vdbVersion = targetAppReg.get( "OpenVDBVersion", cortexReg["OpenVDBVersion"] )
oslVersion = targetAppReg.get( "OpenShadingLanguageVersion", gafferReg.get( "OpenShadingLanguageVersion", cortexReg["OpenShadingLanguageVersion"] ) )
tbbVersion = targetAppReg.get( "tbbVersion", cortexReg["tbbVersion"] )
Expand Down Expand Up @@ -337,6 +338,7 @@ LOCATE_DEPENDENCY_LIBPATH = [
os.path.join( IEEnv.Environment.rootPath(), "apps", "openexr", exrVersion, IEEnv.platform(), compiler, compilerVersion, "python", pythonVersion, "boost", boostVersion, "lib64" ),
os.path.join( IEEnv.Environment.rootPath(), "tools", "lib", IEEnv.platform(), compiler, compilerVersion ),
os.path.join( IEEnv.Environment.rootPath(), "apps", "embree", embreeVersion, IEEnv.platform(), compiler, compilerVersion, "lib" ),
os.path.join( IEEnv.Environment.rootPath(), "apps", "OpenColorIO", ocioVersion, IEEnv.platform(), compiler, compilerVersion, "lib" ),

]

Expand Down Expand Up @@ -487,7 +489,9 @@ if "install" in sys.argv :

IMATH_LIB_SUFFIX = IEEnv.BuildUtil.libSuffix( "OpenEXR", exrVersion )
OIIO_LIB_SUFFIX = IEEnv.BuildUtil.libSuffix( "OpenImageIO", oiioLibSuffix )
OCIO_LIB_SUFFIX = IEEnv.BuildUtil.libSuffix( "OpenColorIO", ocioVersion )
# ocioLibSuffix, when provided, should be used directly, without the addition of "-"
# For example, when set to "Foundry"
OCIO_LIB_SUFFIX = ocioLibSuffix
OSL_LIB_SUFFIX = IEEnv.BuildUtil.libSuffix( "OpenShadingLanguage", oslVersion )
BOOST_LIB_SUFFIX = IEEnv.BuildUtil.libSuffix( "boost", boostVersion, { "compiler" : compiler, "compilerVersion" : compilerVersion } )
BOOST_PYTHON_LIB_SUFFIX = IEEnv.BuildUtil.libSuffix(
Expand Down
8 changes: 8 additions & 0 deletions include/GafferSceneUI/Private/OutputBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

#include "IECore/PathMatcher.h"

#include <filesystem>
#include <mutex>

namespace GafferSceneUI
Expand Down Expand Up @@ -81,6 +82,13 @@ class OutputBuffer
using BufferChangedSignal = Gaffer::Signals::Signal<void()>;
BufferChangedSignal &bufferChangedSignal();

/// See `SceneGadget::snapshotToFile()` for documentation.
void snapshotToFile(
const std::filesystem::path &fileName,
const Imath::Box2f &resolutionGate = Imath::Box2f(),
const IECore::CompoundData *metadata = nullptr
);

private :

class DisplayDriver;
Expand Down
13 changes: 13 additions & 0 deletions include/GafferSceneUI/SceneGadget.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@

#include "IECoreGL/State.h"

#include <filesystem>

namespace GafferSceneUI
{

Expand Down Expand Up @@ -191,6 +193,17 @@ class GAFFERSCENEUI_API SceneGadget : public GafferUI::Gadget
/// Implemented to return the name of the object under the mouse.
std::string getToolTip( const IECore::LineSegment3f &line ) const override;

/// Saves a snapshot of the current rendered scene. All renderers are supported _except_
/// the OpenGL renderer. All formats supported by OpenImageIO can be used. The output
/// display window will be set to `resolutionGate` if it is not an empty `Box2f`.
/// All of the supplied metadata will be written, regardless of conflicts with
/// OpenImageIO built-in metadata.
void snapshotToFile(
const std::filesystem::path &fileName,
const Imath::Box2f &resolutionGate = Imath::Box2f(),
const IECore::CompoundData *metadata = nullptr
) const;

protected :

void renderLayer( Layer layer, const GafferUI::Style *style, RenderReason reason ) const override;
Expand Down
167 changes: 167 additions & 0 deletions python/GafferArnoldTest/InteractiveArnoldRenderTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,173 @@ def testEditLightGroups( self ) :
self.assertEqual( len( script["catalogue"]["images"] ), 1 )
self.assertNotIn( "gaffer:isRendering", script["catalogue"]["out"].metadata() )

## \todo Promote to InteractiveRenderTest and check it works for other renderer backends.
def testEditOutputMetadata( self ) :

script = Gaffer.ScriptNode()
script["catalogue"] = GafferImage.Catalogue()

script["sphere"] = GafferScene.Sphere()

script["outputs"] = GafferScene.Outputs()
script["outputs"]["in"].setInput( script["sphere"]["out"] )

script["outputs"].addOutput(
"beauty",
IECoreScene.Output(
"test",
"ieDisplay",
"rgba",
{
"driverType" : "ClientDisplayDriver",
"displayHost" : "localhost",
"displayPort" : str( script["catalogue"].displayDriverServer().portNumber() ),
"remoteDisplayType" : "GafferImage::GafferDisplayDriver",
"header:test1" : "hello",
"header:test2" : "world",
}
)
)

script["renderer"] = self._createInteractiveRender()
script["renderer"]["in"].setInput( script["outputs"]["out"] )

# Start a render, give it time to finish, and check we have our
# custom metadata in the outputs.

script["renderer"]["state"].setValue( script["renderer"].State.Running )
self.uiThreadCallHandler.waitFor( 1 )

self.assertEqual( len( script["catalogue"]["images"] ), 1 )
self.assertEqual( script["catalogue"]["out"].metadata()["test1"], IECore.StringData( "hello" ) )
self.assertEqual( script["catalogue"]["out"].metadata()["test2"], IECore.StringData( "world" ) )

# Modify the header parameters and rerender.

with Gaffer.DirtyPropagationScope() :

script["outputs"]["outputs"][0]["parameters"]["header_test1"]["name"].setValue( "header:test1B" )
script["outputs"]["outputs"][0]["parameters"]["header_test2"]["value"].setValue( "edited" )

self.uiThreadCallHandler.waitFor( 1 )

self.assertEqual( len( script["catalogue"]["images"] ), 1 )
self.assertNotIn( "test1", script["catalogue"]["out"].metadata() )
self.assertEqual( script["catalogue"]["out"].metadata()["test1B"], IECore.StringData( "hello" ) )
self.assertEqual( script["catalogue"]["out"].metadata()["test2"], IECore.StringData( "edited" ) )

## \todo Promote to InteractiveRenderTest and check it works for other renderer backends.
def testEditOutputType( self ) :

script = Gaffer.ScriptNode()
script["catalogue"] = GafferImage.Catalogue()

script["sphere"] = GafferScene.Sphere()

script["outputs"] = GafferScene.Outputs()
script["outputs"]["in"].setInput( script["sphere"]["out"] )

script["outputs"].addOutput(
"beauty",
IECoreScene.Output(
"test",
"ieDisplay",
"rgba",
{
"driverType" : "ClientDisplayDriver",
"displayHost" : "localhost",
"displayPort" : str( script["catalogue"].displayDriverServer().portNumber() ),
"remoteDisplayType" : "GafferImage::GafferDisplayDriver",
}
)
)

script["renderer"] = self._createInteractiveRender()
script["renderer"]["in"].setInput( script["outputs"]["out"] )

# Start a render, give it time to finish, and check we have an image.

script["renderer"]["state"].setValue( script["renderer"].State.Running )
self.uiThreadCallHandler.waitFor( 1 )

self.assertEqual( len( script["catalogue"]["images"] ), 1 )
self.assertEqual( script["catalogue"]["out"].metadata()["gaffer:isRendering"], IECore.BoolData( True ) )

# Modify the output to render to file instead of the catalogue, and check
# the catalogue image is closed and the file is created.

with Gaffer.DirtyPropagationScope() :

script["outputs"]["outputs"][0]["fileName"].setValue( self.temporaryDirectory() / "test.exr" )
script["outputs"]["outputs"][0]["type"].setValue( "exr" )

self.uiThreadCallHandler.waitFor( 1 )

self.assertEqual( len( script["catalogue"]["images"] ), 1 )
self.assertNotIn( "gaffer:isRendering", script["catalogue"]["out"].metadata() )
self.assertTrue( ( self.temporaryDirectory() / "test.exr" ).is_file() )

## \todo Promote to InteractiveRenderTest and check it works for other renderer backends.
def testEditOutputFilterType( self ) :

script = Gaffer.ScriptNode()
script["catalogue"] = GafferImage.Catalogue()

script["sphere"] = GafferScene.Sphere()

script["outputs"] = GafferScene.Outputs()
script["outputs"]["in"].setInput( script["sphere"]["out"] )

script["outputs"].addOutput(
"beauty",
IECoreScene.Output(
"test",
"ieDisplay",
"rgba",
{
"driverType" : "ClientDisplayDriver",
"displayHost" : "localhost",
"displayPort" : str( script["catalogue"].displayDriverServer().portNumber() ),
"remoteDisplayType" : "GafferImage::GafferDisplayDriver",
"filter" : "gaussian"
}
)
)

script["renderer"] = self._createInteractiveRender()
script["renderer"]["in"].setInput( script["outputs"]["out"] )

# Start a render, give it time to finish, and check we have an image.

script["renderer"]["state"].setValue( script["renderer"].State.Running )
self.uiThreadCallHandler.waitFor( 1 )

self.assertEqual( len( script["catalogue"]["images"] ), 1 )
self.assertEqual( script["catalogue"]["out"].metadata()["gaffer:isRendering"], IECore.BoolData( True ) )

self.assertAlmostEqual(
self._color4fAtUV( script["catalogue"], imath.V2f( 0.5 ) ).r,
1,
delta = 0.01
)

# Modify the output to use a different filter, check we're still
# rendering to the same image in the catalogue, and that the image
# has been affected by the filter.

script["outputs"]["outputs"][0]["parameters"]["filter"]["value"].setValue( "variance" )

self.uiThreadCallHandler.waitFor( 1 )

self.assertEqual( len( script["catalogue"]["images"] ), 1 )
self.assertEqual( script["catalogue"]["out"].metadata()["gaffer:isRendering"], IECore.BoolData( True ) )

self.assertAlmostEqual(
self._color4fAtUV( script["catalogue"], imath.V2f( 0.5 ) ).r,
0,
delta = 0.01
)

def _createConstantShader( self ) :

shader = GafferArnold.ArnoldShader()
Expand Down
21 changes: 21 additions & 0 deletions python/GafferImageTest/ResampleTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,27 @@ def __test( fileName, size, filter ) :
with self.subTest( fileName = args[0], size = args[1], ftilter = args[2] ):
__test( *args )

def testInseparableFastPath( self ) :

reader = GafferImage.ImageReader()
reader["fileName"].setValue( self.imagesPath() / "resamplePatterns.exr" )

# When applying an inseparable filter with no scaling, we can use a much faster code path.
# This code path should not have any effect on the result
resampleFastPath = GafferImage.Resample()
resampleFastPath["in"].setInput( reader["out"] )
resampleFastPath['filterScale'].setValue( imath.V2f( 4 ) )
resampleFastPath["filter"].setValue( "radial-lanczos3" )

# Force the slow code path using the "debug" parameter
resampleReference = GafferImage.Resample()
resampleReference["in"].setInput( reader["out"] )
resampleReference['filterScale'].setValue( imath.V2f( 4 ) )
resampleReference["filter"].setValue( "radial-lanczos3" )
resampleReference["debug"].setValue( GafferImage.Resample.Debug.SinglePass )

self.assertImagesEqual( resampleFastPath["out"], resampleReference["out"] )

def testSincUpsize( self ) :

c = GafferImage.Constant()
Expand Down
Loading

0 comments on commit 0c79105

Please sign in to comment.