17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-17 03:59:19 +02:00

perf(instancing): instances of scene cells culled by copies of their bounds kept with the buckets, before the instances themselves are fetched; instances past the range of the lod stages of their model left out; identity of the instance buffer restored once per bucket instead of after each batch

This commit is contained in:
2026-10-07 16:29:15 +02:00
parent bc99b80191
commit 398fdac6a1
4 changed files with 128 additions and 21 deletions

View File

@@ -379,7 +379,9 @@ basic_cell::insert( TAnimModel *Instance ) {
if( mat != nullptr ) {
for( int i = 0; i < 5; ++i ) { key.skins[i] = mat->replacable_skins[i]; }
}
m_instancebuckets_opaque[ key ].emplace_back( Instance );
auto &bucket { m_instancebuckets_opaque[ key ] };
bucket.instances.emplace_back( Instance );
bucket.bounds.clear();
}
}
// re-calculate cell bounding area, in case model extends outside the cell's boundaries
@@ -478,10 +480,12 @@ basic_cell::erase( TAnimModel *Instance ) {
// NOTE: searched by pointer rather than by key: the instance may have lost its instanceable
// status or changed its skins since it was inserted, which would leave a dangling pointer behind
for( auto bucket = m_instancebuckets_opaque.begin(); bucket != m_instancebuckets_opaque.end(); ) {
bucket->second.erase(
std::remove( std::begin( bucket->second ), std::end( bucket->second ), Instance ),
std::end( bucket->second ) );
if( bucket->second.empty() ) {
auto &instances { bucket->second.instances };
instances.erase(
std::remove( std::begin( instances ), std::end( instances ), Instance ),
std::end( instances ) );
bucket->second.bounds.clear();
if( instances.empty() ) {
bucket = m_instancebuckets_opaque.erase( bucket );
}
else {

View File

@@ -239,6 +239,21 @@ public:
}
};
using instance_bucket_map = std::unordered_map< instance_bucket_key, std::vector<TAnimModel *>, instance_bucket_key_hash >;
// what it takes to tell that an instance is out of sight, copied from the instance.
// a cell can hold thousands of instances, each of them large and placed wherever the memory was free, and a render
// pass rejects most of them. the renderer makes the copies and goes by them, to fetch only the instances it may draw
struct instance_bounds {
glm::vec3 center { 0.f }; // location of the instance, in the precision the frustum test takes it
float radius { 0.f };
float rangesquaredmax { 0.f }; // past it the instance isn't drawn: by its own range, or the lod stages of its model
};
struct instance_bucket {
std::vector<TAnimModel *> instances;
// caches owned by the renderer; emptied whenever the content of the bucket changes
mutable std::vector<instance_bounds> bounds; // copies for the instances, in the same order
mutable unsigned int boundsversion { 0 };
};
using instance_bucketdata_map = std::unordered_map< instance_bucket_key, instance_bucket, instance_bucket_key_hash >;
using sound_sequence = std::vector<sound_source *>;
using eventlauncher_sequence = std::vector<TEventLauncher *>;
using memorycell_sequence = std::vector<TMemCell *>;
@@ -259,7 +274,7 @@ public:
// batched instance buckets keyed by shared TModel3d*; populated alongside
// m_instancesopaque for nodes whose TAnimModel::m_instanceable == true.
// The renderer uses these to amortise per-model state setup across many instances.
instance_bucket_map m_instancebuckets_opaque;
instance_bucketdata_map m_instancebuckets_opaque;
traction_sequence m_traction;
sound_sequence m_sounds;
eventlauncher_sequence m_eventlaunchers;