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

perf(instancing): skip models without shadow casters in shadow passes

This commit is contained in:
2026-10-01 12:57:33 +02:00
parent 2130d175e9
commit d670c4b116
2 changed files with 33 additions and 10 deletions

View File

@@ -3032,6 +3032,12 @@ void opengl33_renderer::Render_Instanced( TModel3d *Model, std::vector<TAnimMode
// far instances at the LOD of the nearest one. Each instance is assigned to its band and every
// band is drawn with its own fSquareDist, like in Render_Sleepers(). The grouping is a counting
// sort, linear in the instance count.
// the same walk also checks whether any piece of the model can get into the shadow map. Models
// whose materials are all excluded by the shadow rank cutoff (e.g. grass) would otherwise go through
// the whole per-instance culling and the batched draws in every shadow pass, only for every submodel
// to be skipped in Render(TSubModel*)
auto const *skins { Instances.front() != nullptr && Instances.front()->Material() != nullptr ? Instances.front()->Material()->replacable_skins : nullptr };
bool castsshadow { m_renderpass.draw_mode != rendermode::shadows };
m_instance_lodbounds.clear();
m_instance_lodpending.clear();
m_instance_lodpending.push_back( Model->Root );
@@ -3041,9 +3047,14 @@ void opengl33_renderer::Render_Instanced( TModel3d *Model, std::vector<TAnimMode
if( submodel == nullptr ) { continue; }
if( submodel->fSquareMinDist > 0.f ) { m_instance_lodbounds.emplace_back( submodel->fSquareMinDist ); }
if( submodel->fSquareMaxDist < std::numeric_limits<float>::max() ) { m_instance_lodbounds.emplace_back( submodel->fSquareMaxDist ); }
if( ( false == castsshadow ) && ( submodel->eType < TP_ROTATOR ) ) {
auto const material { submodel->m_material >= 0 ? submodel->m_material : ( skins != nullptr ? skins[ -submodel->m_material ] : null_handle ) };
castsshadow = ( Material( material ).shadow_rank <= Global.gfx_shadow_rank_cutoff );
}
m_instance_lodpending.push_back( submodel->Child );
m_instance_lodpending.push_back( submodel->Next );
}
if( false == castsshadow ) { return; }
std::sort( m_instance_lodbounds.begin(), m_instance_lodbounds.end() );
m_instance_lodbounds.erase( std::unique( m_instance_lodbounds.begin(), m_instance_lodbounds.end() ), m_instance_lodbounds.end() );
auto const bandcount { m_instance_lodbounds.size() + 1 };
@@ -3064,6 +3075,7 @@ void opengl33_renderer::Render_Instanced( TModel3d *Model, std::vector<TAnimMode
bool prepared_shared_state = false; // RaPrepare() runs once per bucket
material_data const *batch_material { nullptr };
float farthest_distancesquared { 0.f };
for( auto *Instance : Instances ) {
if( Instance == nullptr ) { continue; }
@@ -3112,6 +3124,7 @@ void opengl33_renderer::Render_Instanced( TModel3d *Model, std::vector<TAnimMode
root[ 3 ] = glm::vec4( glm::vec3( Instance->location() - m_renderpass.pass_camera.position() ), 1.f );
auto const distance { static_cast<float>( distancesquared ) };
farthest_distancesquared = std::max( farthest_distancesquared, distance );
m_instance_survivors.push_back( {
view_matrix * root,
distance,
@@ -3120,16 +3133,25 @@ void opengl33_renderer::Render_Instanced( TModel3d *Model, std::vector<TAnimMode
if( m_instance_survivors.empty() ) { return; }
// group the modelviews by lod band (counting sort) into a contiguous array, for the UBO upload.
// every distance in a band selects the same lod; the nearest one represents the band
m_instance_bandstarts.assign( bandcount + 1, 0 );
// group the modelviews by lod band into a contiguous array, for the UBO upload. every distance in
// a band selects the same lod; the nearest one represents the band.
// within a band the instances are additionally ordered roughly near-to-far, in distance slots. dense
// alpha tested foliage then gets the most out of the early depth test, as the nearest instances fill
// the depth buffer first and hide the fragments of those behind them before they get shaded.
// both levels are done with a single counting sort, linear in the instance count
auto const distanceslots { 16 };
auto const keycount { bandcount * distanceslots };
auto const slotscale { distanceslots / std::sqrt( std::max( farthest_distancesquared, 1.f ) ) };
m_instance_bandstarts.assign( keycount + 1, 0 );
m_instance_banddistances.assign( bandcount, std::numeric_limits<float>::max() );
for( auto const &survivor : m_instance_survivors ) {
++m_instance_bandstarts[ survivor.band + 1 ];
for( auto &survivor : m_instance_survivors ) {
m_instance_banddistances[ survivor.band ] = std::min( m_instance_banddistances[ survivor.band ], survivor.distancesquared );
auto const slot { std::min( distanceslots - 1, static_cast<int>( std::sqrt( survivor.distancesquared ) * slotscale ) ) };
survivor.band = static_cast<std::uint32_t>( survivor.band * distanceslots + slot ); // from now on it's the sort key
++m_instance_bandstarts[ survivor.band + 1 ];
}
for( std::size_t band = 1; band <= bandcount; ++band ) {
m_instance_bandstarts[ band ] += m_instance_bandstarts[ band - 1 ];
for( std::size_t key = 1; key <= keycount; ++key ) {
m_instance_bandstarts[ key ] += m_instance_bandstarts[ key - 1 ];
}
m_instance_bandcursors.assign( m_instance_bandstarts.begin(), m_instance_bandstarts.end() - 1 );
m_instance_modelviews.resize( m_instance_survivors.size() );
@@ -3145,8 +3167,8 @@ void opengl33_renderer::Render_Instanced( TModel3d *Model, std::vector<TAnimMode
// instance_modelview[gl_InstanceID] in the shader.
for( std::size_t band = 0; band < bandcount; ++band ) {
std::size_t offset_idx = m_instance_bandstarts[ band ];
std::size_t const band_end = m_instance_bandstarts[ band + 1 ];
std::size_t offset_idx = m_instance_bandstarts[ band * distanceslots ];
std::size_t const band_end = m_instance_bandstarts[ ( band + 1 ) * distanceslots ];
while( offset_idx < band_end ) {
std::size_t const this_batch = std::min<std::size_t>( band_end - offset_idx, gl::MAX_INSTANCES_PER_BATCH );

View File

@@ -436,7 +436,8 @@ class opengl33_renderer : public gfx_renderer {
std::vector<instance_survivor> m_instance_survivors;
std::vector<float> m_instance_lodbounds;
std::vector<TSubModel const *> m_instance_lodpending;
// per lod band: first index in m_instance_modelviews (with end sentinel), fill cursor, nearest distance
// per sort key (lod band and distance slot): first index in m_instance_modelviews (with end sentinel)
// and fill cursor; per lod band: nearest distance
std::vector<std::size_t> m_instance_bandstarts;
std::vector<std::size_t> m_instance_bandcursors;
std::vector<float> m_instance_banddistances;