From 8903d15b945aea8a03d82e445a09d4dc5112d2d8 Mon Sep 17 00:00:00 2001 From: maj00r Date: Wed, 21 Oct 2026 08:41:50 +0200 Subject: [PATCH] fix(editor): a track dragged around no longer piles copies of its geometry up in the graphics memory The geometry of a track made anew, ten times a second while it's dragged, only forgot the chunks it had: the bank kept them all, so the buffer of the section grew by the whole track and its neighbours each time and was sent to the gpu in full again. Ten rebuilds of a test scenery took the bank from 2 to 29 MB; a long session would run the card out of memory. The old chunks are erased from the bank now, but for those of a moving nose, laid again in place, and a moved traction wire drops its old one too. A chunk swapped for one without the extra vertex data kept the flag of having it and the upload read from an empty array; the buffer is made again when that changes. --- application/editormodeinfra.cpp | 1 + rendering/geometrybank.cpp | 22 ++++++++++++++++++++++ rendering/geometrybank.h | 2 ++ rendering/opengl33geometrybank.cpp | 3 ++- rendering/opengl33renderer.cpp | 5 +++++ rendering/opengl33renderer.h | 1 + rendering/openglrenderer.cpp | 5 +++++ rendering/openglrenderer.h | 1 + rendering/renderer.h | 1 + world/Track.cpp | 15 +++++++++++++++ 10 files changed, 55 insertions(+), 1 deletion(-) diff --git a/application/editormodeinfra.cpp b/application/editormodeinfra.cpp index 1840ffe7..db6e4f3b 100644 --- a/application/editormodeinfra.cpp +++ b/application/editormodeinfra.cpp @@ -292,6 +292,7 @@ void move_traction(TTraction *Traction, std::array const &Points) Traction->location(glm::mix(Points[1], Points[0], 0.5)); Traction->m_area.radius = -1.f; simulation::Region->insert_and_register(Traction); + GfxRenderer->Erase(Traction->m_geometry); Traction->m_geometry = gfx::geometry_handle{}; auto §ion{simulation::Region->section(Traction->location())}; if (section.m_geometrycreated) diff --git a/rendering/geometrybank.cpp b/rendering/geometrybank.cpp index f801c450..2a1ff5ee 100644 --- a/rendering/geometrybank.cpp +++ b/rendering/geometrybank.cpp @@ -318,6 +318,20 @@ geometry_bank::append( gfx::vertex_array &Vertices, userdata_array &Userdata, gf return replace( Vertices, Userdata, Geometry, gfx::geometry_bank::chunk( Geometry ).vertices.size() ); } +void +geometry_bank::erase( gfx::geometry_handle const &Geometry ) { + + if( ( Geometry.chunk == 0 ) || ( Geometry.chunk > m_chunks.size() ) ) { return; } + + auto &chunk = gfx::geometry_bank::chunk( Geometry ); + if( chunk.vertices.empty() && chunk.indices.empty() ) { return; } + + gfx::vertex_array().swap( chunk.vertices ); + gfx::index_array().swap( chunk.indices ); + gfx::userdata_array().swap( chunk.userdata ); + replace_( Geometry ); +} + // draws geometry stored in specified chunk std::size_t geometry_bank::draw( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams ) { @@ -425,6 +439,14 @@ geometrybank_manager::append( gfx::vertex_array &Vertices, userdata_array &Userd return bank( Geometry ).first->append( Vertices, Userdata, Geometry ); } + +void +geometrybank_manager::erase( gfx::geometry_handle const &Geometry ) { + + if( ( Geometry.bank == 0 ) || ( Geometry.bank > m_geometrybanks.size() ) ) { return; } + + bank( Geometry ).first->erase( Geometry ); +} // draws geometry stored in specified chunk void geometrybank_manager::draw( gfx::geometry_handle const &Geometry, unsigned int const Streams ) { diff --git a/rendering/geometrybank.h b/rendering/geometrybank.h index 47e7afc7..537c79ba 100644 --- a/rendering/geometrybank.h +++ b/rendering/geometrybank.h @@ -117,6 +117,7 @@ public: auto replace( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool; // adds supplied vertex data at the end of specified chunk auto append( gfx::vertex_array &Vertices, gfx::userdata_array& Userdata, gfx::geometry_handle const &Geometry ) -> bool; + void erase( gfx::geometry_handle const &Geometry ); // draws geometry stored in specified chunk auto draw( gfx::geometry_handle const &Geometry, gfx::stream_units const &Units, unsigned int const Streams = basic_streams ) -> std::size_t; // draws geometry stored in specified chunk N times via glDrawElementsInstanced* @@ -222,6 +223,7 @@ public: auto replace( gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, std::size_t const Offset = 0 ) -> bool; // adds supplied vertex data at the end of specified chunk auto append( gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry ) -> bool; + void erase( gfx::geometry_handle const &Geometry ); // draws geometry stored in specified chunk void draw( gfx::geometry_handle const &Geometry, unsigned int const Streams = basic_streams ); // draws geometry stored in specified chunk InstanceCount times via GPU instancing. diff --git a/rendering/opengl33geometrybank.cpp b/rendering/opengl33geometrybank.cpp index 4713ec69..978a1b75 100644 --- a/rendering/opengl33geometrybank.cpp +++ b/rendering/opengl33geometrybank.cpp @@ -33,7 +33,8 @@ opengl33_vaogeometrybank::replace_( gfx::geometry_handle const &Geometry ) { auto &chunkrecord = m_chunkrecords[ Geometry.chunk - 1 ]; chunkrecord.is_good = false; // if the overall length of the chunk didn't change we can get away with reusing the old buffer... - if( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.vertex_count ) { + if( ( geometry_bank::chunk( Geometry ).vertices.size() != chunkrecord.vertex_count ) + || ( geometry_bank::chunk( Geometry ).userdata.empty() == chunkrecord.has_userdata ) ) { // ...but otherwise we'll need to allocate a new one // TBD: we could keep and reuse the old buffer also if the new chunk is smaller than the old one, // but it'd require some extra tracking and work to keep all chunks up to date; also wasting vram; may be not worth it? diff --git a/rendering/opengl33renderer.cpp b/rendering/opengl33renderer.cpp index b77731cf..b1f9e56d 100644 --- a/rendering/opengl33renderer.cpp +++ b/rendering/opengl33renderer.cpp @@ -2217,6 +2217,11 @@ bool opengl33_renderer::Append(gfx::vertex_array &Vertices, gfx::userdata_array return m_geometry.append(Vertices, Userdata, Geometry); } +void opengl33_renderer::Erase(gfx::geometry_handle const &Geometry) +{ + m_geometry.erase(Geometry); +} + // provides direct access to index data of specfied chunk gfx::index_array const & opengl33_renderer::Indices(gfx::geometry_handle const &Geometry) const { diff --git a/rendering/opengl33renderer.h b/rendering/opengl33renderer.h index a7bd46d6..d7adfb9e 100644 --- a/rendering/opengl33renderer.h +++ b/rendering/opengl33renderer.h @@ -67,6 +67,7 @@ class opengl33_renderer : public gfx_renderer { bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override; // adds supplied vertex data at the end of specified chunk bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override; + void Erase( gfx::geometry_handle const &Geometry ) override; // provides direct access to index data of specfied chunk gfx::index_array const & Indices( gfx::geometry_handle const &Geometry ) const override; diff --git a/rendering/openglrenderer.cpp b/rendering/openglrenderer.cpp index c4351b9e..d20626e3 100644 --- a/rendering/openglrenderer.cpp +++ b/rendering/openglrenderer.cpp @@ -1767,6 +1767,11 @@ bool opengl_renderer::Append(gfx::vertex_array &Vertices, gfx::userdata_array &U return m_geometry.append( Vertices, Userdata, Geometry ); } +void opengl_renderer::Erase( gfx::geometry_handle const &Geometry ) +{ + m_geometry.erase( Geometry ); +} + // provides direct access to vertex data of specfied chunk gfx::index_array const & opengl_renderer::Indices( gfx::geometry_handle const &Geometry ) const { diff --git a/rendering/openglrenderer.h b/rendering/openglrenderer.h index 78fffa40..da3faad0 100644 --- a/rendering/openglrenderer.h +++ b/rendering/openglrenderer.h @@ -65,6 +65,7 @@ public: bool Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) override; // adds supplied vertex data at the end of specified chunk bool Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) override; + void Erase( gfx::geometry_handle const &Geometry ) override; // provides direct access to index data of specfied chunk gfx::index_array const & Indices( gfx::geometry_handle const &Geometry ) const override; diff --git a/rendering/renderer.h b/rendering/renderer.h index 649dd795..2c302a75 100644 --- a/rendering/renderer.h +++ b/rendering/renderer.h @@ -59,6 +59,7 @@ public: virtual auto Replace(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type, const std::size_t Offset = 0) -> bool = 0; // adds supplied vertex data at the end of specified chunk virtual auto Append(gfx::vertex_array &Vertices, gfx::userdata_array &Userdata, gfx::geometry_handle const &Geometry, int const Type) -> bool = 0; + virtual void Erase( gfx::geometry_handle const &Geometry ) {} // provides direct access to index data of specfied chunk virtual auto Indices( gfx::geometry_handle const &Geometry ) const->gfx::index_array const & = 0; // provides direct access to vertex data of specfied chunk diff --git a/world/Track.cpp b/world/Track.cpp index ba24fa9d..ae071220 100644 --- a/world/Track.cpp +++ b/world/Track.cpp @@ -1471,12 +1471,27 @@ glm::vec3 TTrack::get_nearest_point(const glm::dvec3 &point) const void TTrack::rebuild_geometry( gfx::geometrybank_handle const &Bank ) { track_rails::forget(); + auto const erase = [ this ]( gfx::geometry_handle const &Chunk ) { + if( SwitchExtension + && std::any_of( SwitchExtension->m_nosechunks.begin(), SwitchExtension->m_nosechunks.end(), [ & ]( auto const &Pooled ) { return Pooled.handle == Chunk; } ) ) { + return; + } + GfxRenderer->Erase( Chunk ); + }; + for( auto const *geometry : { &Geometry1, &Geometry2, &GeometryCut1, &GeometryCut2 } ) { + std::for_each( geometry->begin(), geometry->end(), erase ); + } + for( auto const &tread : GeometryTreads ) { + std::for_each( tread.geometry.begin(), tread.geometry.end(), erase ); + std::for_each( tread.cuts.begin(), tread.cuts.end(), erase ); + } Geometry1.clear(); Geometry2.clear(); GeometryCut1.clear(); GeometryCut2.clear(); GeometryTreads.clear(); if( SwitchExtension ) { + erase( SwitchExtension->Geometry3 ); SwitchExtension->Geometry3 = gfx::geometry_handle{}; } m_sleeper_local_transforms.clear();