Skip to content

Commit 4678665

Browse files
committed
LiveScene: Added caching for GeometryList.
Querying Nuke repeatedly for a GeoOp GeometryList proved to be really slow for large hierarchy. We introduce a caching mechanism using nested map that cache at 3 levels: - LiveScene* - Op hash - frame LiveScene allows to maintain a global cache between different LiveScene and avoid collision between the LiveScenes' input GeoOp. Op Hash allows to cache for the same Op but with different knob values or connection upstream. Finally, frame allows to cache all of the above for a given frame. When trying to get the GeometryList, we first check if we have any cache hit for the current LiveScene, then we try to find a cache for the current Op hash and finally for a specific frame. If we have a cache miss, we call the actual geometryList( frame ) to get the input GeoOp actual GeometryList and then we add that to the cache before returning the cached GeometryList. To make sure the caching is fast enough, we are avoid hashing using something like MurMurHash and we also want to maintain that nested structure because of the pattern for cache invalidation. For example, if an Op Hash is modified ( input connection changed upstream ), it's likely we can throw away all the cached frames for it.
1 parent 95e2a68 commit 4678665

File tree

2 files changed

+192
-22
lines changed

2 files changed

+192
-22
lines changed

include/IECoreNuke/LiveScene.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,18 @@ class IECORENUKE_API LiveScene : public IECoreScene::SceneInterface
111111
static double timeToFrame( const double& time );
112112
static double frameToTime( const int& frame );
113113

114+
typedef std::map<double, DD::Image::GeometryList> FrameGeometryCache;
115+
typedef std::map<DD::Image::Hash, FrameGeometryCache> OpGeometryCache;
116+
typedef std::map<const LiveScene*, OpGeometryCache> LiveSceneGeometryCache;
117+
114118
void setOp( DD::Image::GeoOp* op );
115119
const DD::Image::GeoOp *getOp() const;
116120

117121
private:
118122

119-
DD::Image::GeoOp *op() const;
120-
DD::Image::GeometryList geometryList( const double* time=nullptr ) const;
123+
DD::Image::GeometryList geometryList( const double& frame ) const;
124+
DD::Image::GeometryList geometryList( DD::Image::Op* op, const double& frame ) const;
125+
DD::Image::GeoInfo* object( const unsigned& index, const double* time=nullptr ) const;
121126

122127
std::string geoInfoPath( const int& index ) const;
123128

@@ -126,6 +131,8 @@ class IECORENUKE_API LiveScene : public IECoreScene::SceneInterface
126131
IECore::PathMatcher m_pathMatcher;
127132
typedef std::map<unsigned, std::string> objectPathMap;
128133
mutable objectPathMap m_objectPathMap;
134+
135+
void cacheGeometryList( const double& frame ) const;
129136
};
130137

131138
IE_CORE_DECLAREPTR( LiveScene );

src/IECoreNuke/LiveScene.cpp

Lines changed: 183 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "IECoreNuke/LiveScene.h"
3636

3737
#include "DDImage/Scene.h"
38+
#include "DDImage/Execute.h"
3839

3940
#include "IECoreNuke/Convert.h"
4041
#include "IECoreNuke/MeshFromNuke.h"
@@ -54,6 +55,8 @@
5455
#include "boost/format.hpp"
5556
#include "boost/tokenizer.hpp"
5657

58+
#include "tbb/recursive_mutex.h"
59+
5760
using namespace IECore;
5861
using namespace IECoreScene;
5962
using namespace IECoreNuke;
@@ -74,6 +77,14 @@ IECore::TransformationMatrixd convertTransformMatrix( DD::Image::Matrix4& from )
7477
return to;
7578
}
7679

80+
tbb::recursive_mutex g_mutex;
81+
82+
LiveScene::LiveSceneGeometryCache &cachedGeometryListMap()
83+
{
84+
static LiveScene::LiveSceneGeometryCache *cache = new LiveScene::LiveSceneGeometryCache();
85+
return *cache;
86+
}
87+
7788
}
7889

7990
const std::string& LiveScene::nameAttribute( "ieName" );
@@ -124,9 +135,13 @@ std::string LiveScene::geoInfoPath( const int& index ) const
124135
}
125136
else
126137
{
127-
auto info = geometryList().object( index );
138+
auto info = object( index );
139+
if ( !info )
140+
{
141+
return "/undefined" + std::to_string( index );
142+
}
128143
std::string nameValue;
129-
if( auto nameAttrib = info.get_group_attribute( GroupType::Group_Object, nameAttribute.data() ) )
144+
if( auto nameAttrib = info->get_group_attribute( GroupType::Group_Object, nameAttribute.data() ) )
130145
{
131146
nameValue = nameAttrib->stdstring();
132147
}
@@ -141,26 +156,164 @@ std::string LiveScene::geoInfoPath( const int& index ) const
141156
}
142157
}
143158

144-
GeometryList LiveScene::geometryList( const double* time ) const
159+
void LiveScene::cacheGeometryList( const double& frame ) const
145160
{
146-
auto oc = OutputContext();
161+
DD::Image::Hash h;
162+
if( auto parent = m_op->parent() )
163+
{
164+
h = static_cast<Op*>( parent )->hash();
165+
}
166+
else
167+
{
168+
h = static_cast<Op*>( m_op )->hash();
169+
}
170+
auto it = cachedGeometryListMap().find( this );
171+
if ( it == cachedGeometryListMap().end() )
172+
{
173+
auto geomList = geometryList( frame );
174+
cachedGeometryListMap()[this][h][frame] = geomList;
175+
}
176+
else
177+
{
178+
auto jit = cachedGeometryListMap()[this].find( h );
179+
if ( jit == cachedGeometryListMap()[this].end() )
180+
{
181+
auto geomList = geometryList( frame );
182+
cachedGeometryListMap()[this][h][frame] = geomList;
183+
}
184+
else
185+
{
186+
auto kit = cachedGeometryListMap()[this][h].find( frame );
187+
if ( kit == cachedGeometryListMap()[this][h].end() )
188+
{
189+
auto geomList = geometryList( frame );
190+
cachedGeometryListMap()[this][h][frame] = geomList;
191+
}
192+
}
193+
}
194+
}
195+
196+
unsigned LiveScene::objects( const double* time) const
197+
{
198+
double frame;
147199
if ( time )
148200
{
149-
oc.setFrame( timeToFrame( *time ) );
201+
frame = timeToFrame( *time );
150202
}
151203
else
152204
{
153-
oc.setFrame( m_op->outputContext().frame() );
205+
frame = m_op->outputContext().frame();
154206
}
207+
208+
cacheGeometryList( frame );
155209

156-
auto nodeInputOp = m_op->node_input( 0, Op::EXECUTABLE_INPUT, &oc );
157-
auto geoOp = dynamic_cast<DD::Image::GeoOp*>( nodeInputOp );
210+
DD::Image::Hash h;
211+
if( auto parent = m_op->parent() )
212+
{
213+
h = static_cast<Op*>( parent )->hash();
214+
}
215+
else
216+
{
217+
h = static_cast<Op*>( m_op )->hash();
218+
}
158219

159-
geoOp->validate(true);
220+
auto cit = cachedGeometryListMap().find( this );
221+
if ( cit != cachedGeometryListMap().end() )
222+
{
223+
auto jit = cachedGeometryListMap()[this].find( h );
224+
if ( jit != cachedGeometryListMap()[this].end() )
225+
{
226+
227+
auto kit = cachedGeometryListMap()[this][h].find( frame );
228+
if ( kit != cachedGeometryListMap()[this][h].end() )
229+
{
230+
return cachedGeometryListMap()[this][h][frame].objects();
231+
}
232+
}
233+
}
234+
235+
return 0;
236+
}
237+
238+
DD::Image::GeoInfo* LiveScene::object( const unsigned& index, const double* time ) const
239+
{
240+
double frame;
241+
if ( time )
242+
{
243+
frame = timeToFrame( *time );
244+
}
245+
else
246+
{
247+
frame = m_op->outputContext().frame();
248+
}
249+
250+
cacheGeometryList( frame );
251+
252+
DD::Image::Hash h;
253+
if( auto parent = m_op->parent() )
254+
{
255+
h = static_cast<Op*>( parent )->hash();
256+
}
257+
else
258+
{
259+
h = static_cast<Op*>( m_op )->hash();
260+
}
261+
262+
auto cit = cachedGeometryListMap().find( this );
263+
if ( cit != cachedGeometryListMap().end() )
264+
{
265+
auto jit = cachedGeometryListMap()[this].find( h );
266+
if ( jit != cachedGeometryListMap()[this].end() )
267+
{
268+
269+
auto kit = cachedGeometryListMap()[this][h].find( frame );
270+
if ( kit != cachedGeometryListMap()[this][h].end() )
271+
{
272+
return &kit->second.object( index );
273+
}
274+
}
275+
}
276+
return nullptr;
277+
}
278+
279+
DD::Image::GeometryList LiveScene::geometryList( DD::Image::Op* op, const double& frame ) const
280+
{
160281
boost::shared_ptr<DD::Image::Scene> scene( new DD::Image::Scene() );
161-
geoOp->build_scene( *scene );
282+
boost::shared_ptr<DD::Image::GeometryList> geo( new DD::Image::GeometryList() );
283+
284+
auto executioner = Execute();
285+
auto executableOp = executioner.generateOp( op, 0, frame );
286+
if ( !executableOp )
287+
{
288+
return *geo;
289+
}
162290

163-
return *scene->object_list();
291+
DD::Image::GeoOp* geoOp = executableOp->geoOp();
292+
if ( !geoOp )
293+
{
294+
return *geo;
295+
}
296+
297+
geoOp->validate(true);
298+
299+
geoOp->get_geometry( *scene, *geo );
300+
301+
return *geo;
302+
}
303+
304+
DD::Image::GeometryList LiveScene::geometryList( const double& frame ) const
305+
{
306+
// Nuke Geometry API is not thread safe so we need to use a mutex here to avoid crashes.
307+
tbb::recursive_mutex::scoped_lock l( g_mutex );
308+
auto result = geometryList( m_op, frame );
309+
310+
if ( !result.objects() && m_op->input( 0 ) )
311+
{
312+
auto particleToGeo = Op::create( "ParticleToGeo", m_op );
313+
result = geometryList( particleToGeo, frame );
314+
}
315+
316+
return result;
164317
}
165318

166319
std::string LiveScene::fileName() const
@@ -190,7 +343,7 @@ Imath::Box3d LiveScene::readBound( double time ) const
190343
{
191344
Imath::Box3d bound;
192345
IECoreScene::SceneInterface::Path rootPath, currentPath;
193-
for( unsigned i=0; i < geometryList( &time ).objects(); ++i )
346+
for( unsigned i=0; i < objects( &time ); ++i )
194347
{
195348
auto nameValue = geoInfoPath( i );
196349
auto result = m_pathMatcher.match( nameValue );
@@ -201,16 +354,21 @@ Imath::Box3d LiveScene::readBound( double time ) const
201354
IECoreScene::SceneInterface::stringToPath( m_rootPath, rootPath );
202355
IECoreScene::SceneInterface::stringToPath( nameValue, currentPath );
203356

204-
GeoInfo info = geometryList( &time ).object( i );
357+
auto info = object( i, &time );
358+
if ( !info )
359+
{
360+
return bound;
361+
}
362+
205363
Box3 objectBound;
206364
if ( ( currentPath.size() > 1 ) && ( ( currentPath.size() == rootPath.size() + 1 ) || ( nameValue == m_rootPath ) ) )
207365
{
208366
// object space bound
209-
objectBound = info.bbox();
367+
objectBound = info->bbox();
210368
}
211369
else
212370
{
213-
objectBound = info.getTransformedBBox();
371+
objectBound = info->getTransformedBBox();
214372
}
215373
Imath::Box3d b = IECore::convert<Imath::Box3d, Box3>( objectBound );
216374

@@ -231,14 +389,18 @@ void LiveScene::writeBound( const Imath::Box3d &bound, double time )
231389
ConstDataPtr LiveScene::readTransform( double time ) const
232390
{
233391

234-
for( unsigned i=0; i < geometryList().objects(); ++i )
392+
for( unsigned i=0; i < objects( &time ); ++i )
235393
{
236394
auto nameValue = geoInfoPath( i );
237395
auto result = m_pathMatcher.match( nameValue );
238396
if ( result == IECore::PathMatcher::ExactMatch )
239397
{
240-
auto geoInfo = geometryList( &time ).object( i );
241-
auto from = geoInfo.matrix;
398+
auto geoInfo = object( i, &time );
399+
if( !geoInfo )
400+
{
401+
return new TransformationMatrixdData( IECore::TransformationMatrixd() );
402+
}
403+
auto from = geoInfo->matrix;
242404
return new TransformationMatrixdData( convertTransformMatrix( from ) );
243405
}
244406
}
@@ -310,7 +472,7 @@ void LiveScene::hashSet( const Name& setName, IECore::MurmurHash &h ) const
310472

311473
bool LiveScene::hasObject() const
312474
{
313-
for( unsigned i=0; i < geometryList().objects(); ++i )
475+
for( unsigned i=0; i < objects(); ++i )
314476
{
315477
auto nameValue = geoInfoPath( i );
316478
auto result = m_pathMatcher.match( nameValue );
@@ -352,10 +514,11 @@ void LiveScene::writeObject( const Object *object, double time )
352514

353515
void LiveScene::childNames( NameList &childNames ) const
354516
{
517+
355518
childNames.clear();
356519
std::vector<std::string> allPaths;
357520

358-
for( unsigned i=0; i < geometryList().objects(); ++i )
521+
for( unsigned i=0; i < objects(); ++i )
359522
{
360523
auto nameValue = geoInfoPath( i );
361524
auto result = m_pathMatcher.match( nameValue );

0 commit comments

Comments
 (0)