Skip to content

Commit

Permalink
fixup! Instancer : Optimize encapsulated case
Browse files Browse the repository at this point in the history
  • Loading branch information
danieldresser-ie committed Oct 21, 2023
1 parent 1fc65d8 commit 3ae6f19
Showing 1 changed file with 51 additions and 125 deletions.
176 changes: 51 additions & 125 deletions src/GafferScene/Instancer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2729,8 +2729,6 @@ void Instancer::InstancerCapsule::render( IECoreScenePreview::Renderer *renderer
const std::vector<int> &instancesForPrototype = engines[0]->instancesForPrototype( root().back() );
const ScenePlug::ScenePath *prototypeRoot = engines[0]->prototypeRoot( root().back() );

bool hasAttributes = engines[0]->numInstanceAttributes() > 0;

const ScenePlug *prototypesPlug = m_instancer->prototypesPlug();

ObjectVectorPtr prototypesData = new ObjectVector();
Expand Down Expand Up @@ -2791,85 +2789,7 @@ void Instancer::InstancerCapsule::render( IECoreScenePreview::Renderer *renderer
);
}

// If we stick with this approach in the long run, we need some sort of registry of registry or query
// for whether a renderer supports this special instancer object - or perhaps once we standardize an
// instancer object, all renderers will be required to support it.
bool rendererSupportsInstancerObject = renderer->name() == "Arnold";

if( rendererSupportsInstancerObject )
{
ObjectVectorPtr instanceMatrixData = new ObjectVector();
std::vector< std::vector<M44f> * > instanceMatrix;

for( unsigned int i = 0; i < sampleTimes.size(); i++ )
{
M44fVectorDataPtr motionKey = new M44fVectorData();
instanceMatrixData->members().push_back( motionKey );
instanceMatrix.push_back( &motionKey->writable() );
}

std::vector<int> instanceIdsForPrototype;
instanceIdsForPrototype.reserve( instancesForPrototype.size() );
for( int i : instancesForPrototype )
{
instanceIdsForPrototype.push_back( engines[0]->instanceId( i ) );
}

std::vector< std::vector< int > > pointIndices( sampleTimes.size() );

// For the first sample, the pointIndices can just come from instancesForPrototype.
// It's wasteful to duplicate them, but it's better than making all the calls to
// pointIndex() from the loop below. This would all get much simpler if we made it
// an exception to vary pointIndices during the shutter.
pointIndices[0] = instancesForPrototype;

for( unsigned int i = 1; i < sampleTimes.size(); i++ )
{
pointIndices[i].reserve( sampleTimes.size() );
for( int instanceId : instanceIdsForPrototype )
{
pointIndices[i].push_back( engines[i]->pointIndex( instanceId ) );
}
}

// \todo - it seems like there is an opportunity to cut our expansion time by up to half by
// skipping this if we can somehow detect early that the points are not moving.
//
// Currently, our expansion time is a tenth of Arnold's "node init" for a heavy instancer
// though, so it doesn't seem super high priority.
//
// TODO - prep instanceMatrix for the non-Arnold case as well to reduce code duplication?
for( unsigned int i = 0; i < sampleTimes.size(); i++ )
{
instanceMatrix[i]->reserve( instancesForPrototype.size() );
for( unsigned int idx = 0; idx < pointIndices[i].size(); idx++ )
{
int pointIndex = pointIndices[i][idx];
int prototypeIndex = 0;
if( prototypeIndices.size() )
{
prototypeIndex = prototypeIndices[ idx ];
}

instanceMatrix[i]->push_back( prototypeTransforms[ sampleTimes.size() * prototypeIndex + i ] * engines[i]->instanceTransform( pointIndex ) );
}
}

CompoundObjectPtr instanceAttributes = new CompoundObject();
engines[0]->instanceAttributeVectors( pointIndices[0], *instanceAttributes );

IECore::CompoundObjectPtr instancerArguments = new IECore::CompoundObject();
instancerArguments->members()["isInstancer"] = new BoolData( true );
instancerArguments->members()["prototypeIndices"] = prototypeIndicesData;
instancerArguments->members()["prototypes"] = prototypesData;
instancerArguments->members()["prototypeAttributes"] = prototypeAttributesData;
instancerArguments->members()["sampleTimes"] = sampleTimesData;
instancerArguments->members()["instanceMatrix"] = instanceMatrixData;
instancerArguments->members()["instanceAttributes"] = instanceAttributes;

renderer->object( "instancer", instancerArguments.get(), renderer->attributes( new IECore::CompoundObject() ).get() );
return;
}
bool hasAttributes = engines[0]->numInstanceAttributes() > 0;

// If we don't have per-instance attributes, we can pre-prepare renderer attributes ready to go that
// depend only on the prototype
Expand All @@ -2885,58 +2805,64 @@ void Instancer::InstancerCapsule::render( IECoreScenePreview::Renderer *renderer

IECoreScenePreview::Renderer::AttributesInterfacePtr attribsStorage;

vector<M44f> pointTransforms( sampleTimes.size() );
std::string name;
for( unsigned int idx = 0; idx < instancesForPrototype.size(); idx++ )
{
int pointIndex = instancesForPrototype[idx];

int prototypeIndex = 0;
if( prototypeIndices.size() )
tbb::parallel_for( tbb::blocked_range<size_t>( 0, instancesForPrototype.size() ), [&]( const tbb::blocked_range<size_t> &r )
{
prototypeIndex = prototypeIndices[ idx ];
}
vector<M44f> pointTransforms( sampleTimes.size() );
std::string name;

IECoreScenePreview::Renderer::AttributesInterface *attribs;
int instanceId = engines[0]->instanceId( pointIndex );
if( hasAttributes )
{
CompoundObjectPtr currentAttributes = new CompoundObject();
engines[0]->instanceAttributes( pointIndex, *currentAttributes );
for( auto &i : static_cast< CompoundObject* >( prototypeAttributes[prototypeIndex].get() )->members() )
for( size_t idx = r.begin(); idx != r.end(); ++idx )
{
currentAttributes->members()[i.first] = i.second;
}
int pointIndex = instancesForPrototype[idx];

attribsStorage = renderer->attributes( currentAttributes.get() );
attribs = attribsStorage.get();
}
else
{
attribs = prototypeRendererAttribs[prototypeIndex].get();
}
int prototypeIndex = 0;
if( prototypeIndices.size() )
{
prototypeIndex = prototypeIndices[ idx ];
}

// We are running inside a procedural, so we don't need globally unique name. We are making a whole
// lot of these names for instances, so we make these names as absolutely minimal as possible.
name.resize( std::numeric_limits< int >::digits10 + 1 );
name.resize( std::to_chars( &name[0], &(*name.end()), instanceId ).ptr - &name[0] );
IECoreScenePreview::Renderer::AttributesInterface *attribs;
int instanceId = engines[0]->instanceId( pointIndex );
if( hasAttributes )
{
CompoundObjectPtr currentAttributes = new CompoundObject();
engines[0]->instanceAttributes( pointIndex, *currentAttributes );
for( auto &i : static_cast< CompoundObject* >( prototypeAttributes[prototypeIndex].get() )->members() )
{
currentAttributes->members()[i.first] = i.second;
}

IECoreScenePreview::Renderer::ObjectInterfacePtr objectInterface = renderer->object( name, prototypes[prototypeIndex].get(), attribs );
attribsStorage = renderer->attributes( currentAttributes.get() );
attribs = attribsStorage.get();
}
else
{
attribs = prototypeRendererAttribs[prototypeIndex].get();
}

if( sampleTimes.size() == 1 )
{
objectInterface->transform( prototypeTransforms[ prototypeIndex ] * engines[0]->instanceTransform( pointIndex ) );
}
else
{
for( unsigned int i = 0; i < engines.size(); i++ )
{
int curPointIndex = i == 0 ? pointIndex : engines[i]->pointIndex( instanceId );
pointTransforms[i] = prototypeTransforms[ prototypeIndex * sampleTimes.size() + i] * engines[i]->instanceTransform( curPointIndex );
}
// We are running inside a procedural, so we don't need globally unique name. We are making a whole
// lot of these names for instances, so we make these names as absolutely minimal as possible.
name.resize( std::numeric_limits< int >::digits10 + 1 );
name.resize( std::to_chars( &name[0], &(*name.end()), instanceId ).ptr - &name[0] );

objectInterface->transform( pointTransforms, sampleTimes );
}
IECoreScenePreview::Renderer::ObjectInterfacePtr objectInterface = renderer->object( name, prototypes[prototypeIndex].get(), attribs );

}
if( sampleTimes.size() == 1 )
{
objectInterface->transform( prototypeTransforms[ prototypeIndex ] * engines[0]->instanceTransform( pointIndex ) );
}
else
{
for( unsigned int i = 0; i < engines.size(); i++ )
{
int curPointIndex = i == 0 ? pointIndex : engines[i]->pointIndex( instanceId );
pointTransforms[i] = prototypeTransforms[ prototypeIndex * sampleTimes.size() + i] * engines[i]->instanceTransform( curPointIndex );
}

objectInterface->transform( pointTransforms, sampleTimes );
}

}
}
);
}

0 comments on commit 3ae6f19

Please sign in to comment.