mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-09 00:17:39 +01:00
Instancing: skip the buckets of a cell lying wholly beyond the range of their instances (#225)
* perf(instancing): skip a cell's buckets lying wholly beyond the range of their instances * perf(instancing): go only through the instances drawn one by one in the per-node pass * perf(instancing): leave out of the color pass the cells holding only instances too far to be drawn * refactor(instancing): keep the caches of the instances of a cell without mutable fields, set by the renderer when the cell is queued
This commit is contained in:
@@ -2666,6 +2666,12 @@ void opengl33_renderer::Render(section_sequence::iterator First, section_sequenc
|
||||
{
|
||||
if ((true == cell.m_active) && (m_renderpass.pass_camera.visible(cell.m_area)))
|
||||
{
|
||||
// a cell with nothing but instances too far to be drawn is left out of the color pass altogether
|
||||
if( ( true == Prepare_Instances( cell ) )
|
||||
&& ( m_renderpass.draw_mode == rendermode::color )
|
||||
&& ( true == Cell_Out_Of_Range( cell ) ) ) {
|
||||
continue;
|
||||
}
|
||||
// store visible cells with content as well as their current distance, for sorting later
|
||||
m_cellqueue.emplace_back(glm::length2(m_renderpass.pass_camera.position() - cell.m_area.center), &cell);
|
||||
}
|
||||
@@ -2680,6 +2686,7 @@ void opengl33_renderer::Render(section_sequence::iterator First, section_sequenc
|
||||
{
|
||||
if ((true == cell.m_active) && (m_renderpass.pass_camera.visible(cell.m_area)))
|
||||
{
|
||||
Prepare_Instances( cell );
|
||||
// store visible cells with content as well as their current distance, for sorting later
|
||||
m_cellqueue.emplace_back(glm::length2(m_renderpass.pass_camera.position() - cell.m_area.center), &cell);
|
||||
}
|
||||
@@ -2871,8 +2878,10 @@ void opengl33_renderer::Render(cell_sequence::iterator First, cell_sequence::ite
|
||||
Queue_Instances( cell );
|
||||
}
|
||||
}
|
||||
// remaining (non-instanceable) opaque instance nodes go through the per-node path
|
||||
for (auto *instance : cell->m_instancesopaque)
|
||||
// remaining (non-instanceable) opaque instance nodes go through the per-node path. the list of them kept for
|
||||
// the cell spares going through every instance of it only to skip the instanceable ones - with many of these
|
||||
// (vegetation) that took more time than drawing everything else
|
||||
for (auto *instance : ( Instance_Cache_Valid( *cell ) ? cell->m_instancecache.single : cell->m_instancesopaque ))
|
||||
{
|
||||
if( instance->m_instanceable ) { continue; } // already handled via bucket
|
||||
Render(instance);
|
||||
@@ -3178,6 +3187,11 @@ void opengl33_renderer::Queue_Instances( scene::basic_cell const *Cell )
|
||||
auto const distancelimit { slack * ( reflections ? sq( static_cast<double>( Global.reflectiontune.range_instances ) ) : sq( static_cast<double>( m_renderpass.draw_range ) + 250.0 ) ) };
|
||||
// up close the rounding of the copies is too large a part of the distance for the slack to cover it
|
||||
auto const distancetested { sq( 64.0 ) };
|
||||
// the nearest point of the bounding sphere of the cell, which holds the locations of all its instances. no instance
|
||||
// of the cell is closer, so where this distance fails the range test below for the largest range in a bucket,
|
||||
// it fails it for each instance of that bucket, and the bucket can be skipped without going through them
|
||||
auto const cellnearestdistance { std::max( 0.0, glm::length( Cell->m_area.center - cameraposition ) - Cell->m_area.radius ) };
|
||||
auto const cellnearestdistancesquared { cellnearestdistance * cellnearestdistance };
|
||||
|
||||
for( auto const &bucket : Cell->m_instancebuckets_opaque ) {
|
||||
auto const &instances { bucket.second.instances };
|
||||
@@ -3190,6 +3204,11 @@ void opengl33_renderer::Queue_Instances( scene::basic_cell const *Cell )
|
||||
|| ( bucket.second.bounds.size() != instances.size() ) ) {
|
||||
Update_Instance_Bounds( bucket.first.pModel, bucket.second );
|
||||
}
|
||||
else if( ( bucket.second.rangeversion == m_instanceboundsversion )
|
||||
&& ( cellnearestdistancesquared > distancetested )
|
||||
&& ( cellnearestdistancesquared * distancescale + distanceoffset >= slack * bucket.second.rangesquaredmax ) ) {
|
||||
continue;
|
||||
}
|
||||
auto const &bounds { bucket.second.bounds };
|
||||
std::vector<TAnimModel *> *dest { nullptr }; // looked up when there's something to put in it
|
||||
for( std::size_t idx = 0; idx < bounds.size(); ++idx ) {
|
||||
@@ -3210,6 +3229,76 @@ void opengl33_renderer::Queue_Instances( scene::basic_cell const *Cell )
|
||||
}
|
||||
}
|
||||
|
||||
// brings up to date what the renderer keeps of the instances of the cell: the copies of the bounds of the instances
|
||||
// and the largest range in each bucket, the largest range in the cell, and the list of its instances drawn one by one.
|
||||
// rebuilt after the content of the cell changed, or any instance lost its instanceable status (an event added an
|
||||
// animation container, or changed a skin). returns: true if the cache can be used; it can't in the editor, which
|
||||
// moves and resizes the instances in place, as with the copies of their bounds
|
||||
bool opengl33_renderer::Prepare_Instances( scene::basic_cell &Cell )
|
||||
{
|
||||
if( EditorModeFlag ) { return false; }
|
||||
if( Instance_Cache_Valid( Cell ) ) { return true; }
|
||||
|
||||
auto &cache { Cell.m_instancecache };
|
||||
cache.rangesquaredmax = 0.f;
|
||||
for( auto &[ key, bucket ] : Cell.m_instancebuckets_opaque ) {
|
||||
if( ( bucket.boundsversion != m_instanceboundsversion )
|
||||
|| ( bucket.bounds.size() != bucket.instances.size() ) ) {
|
||||
Update_Instance_Bounds( key.pModel, bucket );
|
||||
}
|
||||
auto rangesquaredmax { 0.f };
|
||||
for( auto const &instancebounds : bucket.bounds ) {
|
||||
rangesquaredmax = std::max( rangesquaredmax, instancebounds.rangesquaredmax );
|
||||
}
|
||||
bucket.rangesquaredmax = rangesquaredmax;
|
||||
bucket.rangeversion = m_instanceboundsversion;
|
||||
cache.rangesquaredmax = std::max( cache.rangesquaredmax, rangesquaredmax );
|
||||
}
|
||||
cache.single.clear();
|
||||
for( auto *instance : Cell.m_instancesopaque ) {
|
||||
if( false == instance->m_instanceable ) {
|
||||
cache.single.emplace_back( instance );
|
||||
}
|
||||
}
|
||||
cache.instancedonly =
|
||||
( false == Cell.m_instancebuckets_opaque.empty() )
|
||||
&& Cell.m_paths.empty()
|
||||
&& Cell.m_shapesopaque.empty()
|
||||
&& Cell.m_shapestranslucent.empty()
|
||||
&& Cell.m_lines.empty()
|
||||
&& Cell.m_traction.empty()
|
||||
&& Cell.m_instancetranslucent.empty()
|
||||
&& cache.single.empty();
|
||||
cache.boundsversion = m_instanceboundsversion;
|
||||
cache.revoked = TAnimModel::s_instanceable_revoked;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool opengl33_renderer::Instance_Cache_Valid( scene::basic_cell const &Cell ) const
|
||||
{
|
||||
return ( false == EditorModeFlag )
|
||||
&& ( Cell.m_instancecache.boundsversion == m_instanceboundsversion )
|
||||
&& ( Cell.m_instancecache.revoked == TAnimModel::s_instanceable_revoked );
|
||||
}
|
||||
|
||||
// true if nothing of the cell would be drawn by the color pass: it holds only instances drawn with the instanced path,
|
||||
// and the nearest point of the cell is past the largest range of them. the test is the one made for each bucket in
|
||||
// Queue_Instances(), done before the cell is put in the cell queue: a scenery with many small instances of a short
|
||||
// range has them in nearly every cell up to the draw range, and queueing, sorting and visiting these cells only
|
||||
// to skip all their buckets took a good part of the frame
|
||||
bool opengl33_renderer::Cell_Out_Of_Range( scene::basic_cell const &Cell ) const
|
||||
{
|
||||
if( ( false == Instance_Cache_Valid( Cell ) ) || ( false == Cell.m_instancecache.instancedonly ) ) { return false; }
|
||||
// same measures as in Queue_Instances() for the color pass
|
||||
auto const distancescale { 1.0 / ( static_cast<double>( Global.ZoomFactor ) * static_cast<double>( Global.ZoomFactor ) * static_cast<double>( Global.fDistanceFactor ) ) };
|
||||
auto const slack { 1.001 };
|
||||
auto const distancetested { sq( 64.0 ) };
|
||||
auto const cellnearestdistance { std::max( 0.0, glm::length( Cell.m_area.center - m_renderpass.pass_camera.position() ) - Cell.m_area.radius ) };
|
||||
auto const cellnearestdistancesquared { cellnearestdistance * cellnearestdistance };
|
||||
return ( cellnearestdistancesquared > distancetested )
|
||||
&& ( cellnearestdistancesquared * distancescale >= slack * Cell.m_instancecache.rangesquaredmax );
|
||||
}
|
||||
|
||||
// makes the copies of the bounds of the instances held by a bucket of a scene cell
|
||||
void opengl33_renderer::Update_Instance_Bounds( TModel3d const *Model, scene::basic_cell::instance_bucket const &Bucket )
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user