From aff225c95fb1091de2d436e1d075d56c8e634c90 Mon Sep 17 00:00:00 2001 From: Hirek193 Date: Sat, 23 May 2026 19:28:29 +0200 Subject: [PATCH] Global frame buckets --- rendering/opengl33renderer.cpp | 26 ++++++++++++++++++++++---- rendering/opengl33renderer.h | 6 ++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/rendering/opengl33renderer.cpp b/rendering/opengl33renderer.cpp index 5bb28e52..28ac2534 100644 --- a/rendering/opengl33renderer.cpp +++ b/rendering/opengl33renderer.cpp @@ -2374,6 +2374,8 @@ void opengl33_renderer::Render(scene::basic_region *Region) m_sectionqueue.clear(); m_cellqueue.clear(); + // discard last pass's accumulated instance buckets before this pass starts + m_frame_instance_buckets.clear(); // build a list of region sections to render glm::vec3 const cameraposition{m_renderpass.pass_camera.position()}; auto const camerax = static_cast(std::floor(cameraposition.x / scene::EU07_SECTIONSIZE + scene::EU07_REGIONSIDESECTIONCOUNT / 2)); @@ -2679,9 +2681,12 @@ void opengl33_renderer::Render(cell_sequence::iterator First, cell_sequence::ite case rendermode::shadows: { // TBD, TODO: refactor in to a method to reuse in branch below? - // opaque parts of instanced models -- batched path first + // opaque parts of instanced models -- accumulate this cell's buckets + // into the frame-level map; the actual Render_Instanced() calls are + // issued once per unique model after the cell loop (see flush below). for( auto const &bucket : cell->m_instancebuckets_opaque ) { - Render_Instanced( bucket.first.pModel, bucket.second ); + auto &dest = m_frame_instance_buckets[ bucket.first ]; + dest.insert( dest.end(), bucket.second.begin(), bucket.second.end() ); } // remaining (non-instanceable) opaque instance nodes go through the per-node path for (auto *instance : cell->m_instancesopaque) @@ -2702,9 +2707,11 @@ void opengl33_renderer::Render(cell_sequence::iterator First, cell_sequence::ite case rendermode::reflections: { if( Global.reflectiontune.fidelity >= 1 ) { - // opaque parts of instanced models -- batched path first + // opaque parts of instanced models -- accumulate into the + // frame-level map; flushed once per unique model after the loop. for( auto const &bucket : cell->m_instancebuckets_opaque ) { - Render_Instanced( bucket.first.pModel, bucket.second ); + auto &dest = m_frame_instance_buckets[ bucket.first ]; + dest.insert( dest.end(), bucket.second.begin(), bucket.second.end() ); } for( auto *instance : cell->m_instancesopaque ) { if( instance->m_instanceable ) { continue; } @@ -2744,6 +2751,17 @@ void opengl33_renderer::Render(cell_sequence::iterator First, cell_sequence::ite ++first; } + + // flush accumulated instance buckets: issue one Render_Instanced() per unique + // (TModel3d*, skins) key across every cell visited in this pass, instead of + // one call per cell. All per-instance frustum/distance/pixel-area culling + // still happens inside Render_Instanced(), so correctness is unchanged -- only + // the number of calls and glBufferSubData round-trips drops sharply. + // (For pickscenery/pickcontrols modes the map stays empty, so this is a no-op.) + for( auto const &bucket : m_frame_instance_buckets ) { + Render_Instanced( bucket.first.pModel, bucket.second ); + } + m_frame_instance_buckets.clear(); } void opengl33_renderer::Draw_Geometry(std::vector::iterator begin, std::vector::iterator end) diff --git a/rendering/opengl33renderer.h b/rendering/opengl33renderer.h index 7be76ee0..56069e0f 100644 --- a/rendering/opengl33renderer.h +++ b/rendering/opengl33renderer.h @@ -366,6 +366,12 @@ class opengl33_renderer : public gfx_renderer { renderpass_config m_renderpass; // parameters for current render pass section_sequence m_sectionqueue; // list of sections in current render pass cell_sequence m_cellqueue; + // frame-level accumulation of per-cell opaque instance buckets. Each visited + // cell's buckets are merged here keyed by (TModel3d*, skins), so that + // Render_Instanced() runs once per unique model across the whole pass instead + // of once per cell -- collapsing many tiny instanced draws into a few large + // batches. Reused every pass; cleared at the top of Render(scene::basic_region*). + scene::basic_cell::instance_bucket_map m_frame_instance_buckets; renderpass_config m_colorpass; // parametrs of most recent color pass std::array m_shadowpass; // parametrs of most recent shadowmap pass for each of csm stages std::vector m_pickcontrolsitems;