From bdd89c5ffb6dfbcfff4e4e6a3f84a5ccb33f3a20 Mon Sep 17 00:00:00 2001 From: Seb James Date: Wed, 17 Dec 2025 17:08:01 +0000 Subject: [PATCH 01/15] Beginning the architectural changes required to share SSBOs between instanced VisualModels --- mplot/VisualBase.h | 18 ++++++++++++ mplot/VisualModelBase.h | 20 +++---------- mplot/VisualModelImplMX.h | 61 ++++++++++++++++++--------------------- mplot/VisualOwnableMX.h | 42 +++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 49 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 2c176cf8..2973ce36 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -381,6 +381,24 @@ namespace mplot //! Strength of the diffuse light source float diffuse_intensity = 0.0f; + //! Instanced rendering mode (SSBO access). position data stored in SSBO index 1 (must match GLSL code) + static constexpr unsigned int instance_index = 1; + //! colour, scale, rotation stored in SSBO index 2 + static constexpr unsigned int instparam_index = 2; + //! one 3D vector is 3 floats + static constexpr unsigned int floats_per_instance = 3; + //! Instance params are: colour/alpha (4 floats), scale (1 float) + static constexpr unsigned int floats_per_instparam = 5; + + //! This will control how much GPU RAM is allocated when using instanced rendering (the RAM + //! is *only* allocated if at least one VisualModel is 'instanced'). + static constexpr unsigned int max_instances = 256 * 1024; + static constexpr unsigned int max_instance_floats = floats_per_instance * max_instances; + static constexpr unsigned int max_instparam_floats = floats_per_instparam * max_instances; + constexpr unsigned int max_instanced_items() { return max_instances; } + + //static void init_instance_data (mplot::VisualBase* _v) = 0; + //! Compute position and rotation of coordinate arrows in the bottom left of the screen void positionCoordArrows() { diff --git a/mplot/VisualModelBase.h b/mplot/VisualModelBase.h index 40902964..8989cdf9 100644 --- a/mplot/VisualModelBase.h +++ b/mplot/VisualModelBase.h @@ -718,22 +718,6 @@ namespace mplot GLuint idx = 0u; GLuint idx_bb = 0u; - //! Instanced rendering mode (SSBO access). position data stored in SSBO index 1 (must match GLSL code) - static constexpr unsigned int instance_index = 1; - //! colour, scale, rotation stored in SSBO index 2 - static constexpr unsigned int instparam_index = 2; - //! one 3D vector is 3 floats - static constexpr unsigned int floats_per_instance = 3; - //! Instance params are: colour/alpha (4 floats), scale (1 float) - static constexpr unsigned int floats_per_instparam = 5; - //! later: - // static constexpr unsigned int floats_per_instrotn = 4; - //! This will control how much GPU RAM is allocated when using instanced rendering (the RAM - //! is *only* allocated if this VisualModel is 'instanced'). - static constexpr unsigned int max_instances = 256 * 1024; - static constexpr unsigned int max_instance_floats = floats_per_instance * max_instances; - static constexpr unsigned int max_instparam_floats = floats_per_instparam * max_instances; - constexpr unsigned int max_instanced_items() { return max_instances; } //! If drawing with instancing, how many instances? unsigned int instance_count = 0; //! Which datum in the instance buffer is the one to be drawn by the first thread? @@ -758,6 +742,10 @@ namespace mplot //! Release OpenGL context. Should call parentVis->releaseContext(). std::function*)> releaseContext; + //! Init the SSBOs for instanced rendering + std::function*)> init_instance_data; + std::function*, std::size_t)> resize_instance_data; + //! Set up the instance positions (with default params for colour, rotn, scale) virtual void set_instance_data (const sm::vvec>& position) = 0; diff --git a/mplot/VisualModelImplMX.h b/mplot/VisualModelImplMX.h index a9e8c1bf..d894dc5d 100644 --- a/mplot/VisualModelImplMX.h +++ b/mplot/VisualModelImplMX.h @@ -71,17 +71,6 @@ namespace mplot model->releaseContext = &mplot::VisualBase::release_context; } - void init_instance_data() - { - if constexpr (mplot::gl::version::has_ssbo (glver) == true) { - GladGLContext* _glfn = this->get_glfn (this->parentVis); - if (this->instance_data.ready() == false) { this->instance_data.init (_glfn); } - if (this->instparam_data.ready() == false) { this->instparam_data.init (_glfn); } - } else { - throw std::runtime_error ("Instanced rendering requires OpenGL 4.3 or higher"); - } - } - void set_instance_data (const sm::vvec>& position) final { sm::vvec> c = { mplot::colour::crimson }; @@ -105,15 +94,24 @@ namespace mplot const sm::vvec& alpha, const sm::vvec& scale) final { if (position.size() < 1) { throw std::runtime_error ("set_instance_data: pass some instance positions in"); } - if (position.size() > this->max_instanced_items()) { throw std::runtime_error ("set_instance_data: not enough space"); } + //if (position.size() > this->max_instanced_items()) { throw std::runtime_error ("set_instance_data: not enough space"); } + std::cout << "Colour, alpha, scale sizes: " + << colour.size() << ", " << alpha.size() << ", " << scale.size() << std::endl; +#if 0 size_t j = 0; - this->instance_data.data.resize (mplot::VisualModelBase::floats_per_instance * position.size()); + resize_instance_data (this->parentVis, position.size()); // callback to parent. + + //this->instance_data.data.resize (mplot::VisualBase::floats_per_instance * position.size()); + for (size_t i = 0; i < position.size(); ++i) { sm::vec p = position[i]; - this->instance_data.data[j++] = p[0]; - this->instance_data.data[j++] = p[1]; - this->instance_data.data[j++] = p[2]; + this->push_instance_data (this->parentVis, p[0]); + this->push_instance_data (this->parentVis, p[1]); + this->push_instance_data (this->parentVis, p[2]); + //this->instance_data.data[j++] = p[0]; + //this->instance_data.data[j++] = p[1]; + //this->instance_data.data[j++] = p[2]; } this->instance_count = position.size(); @@ -121,19 +119,22 @@ namespace mplot throw std::runtime_error ("set_instance_data: params vvecs should all have same size (colour, rotn, scale)"); } - this->instparam_data.data.resize (mplot::VisualModelBase::floats_per_instparam * colour.size()); - j = 0; + // resize_instance_data also does this: + //this->instparam_data.data.resize (mplot::VisualBase::floats_per_instparam * colour.size()); j = 0; for (size_t i = 0; i < colour.size(); ++i) { - this->instparam_data.data[j++] = colour[i][0]; - this->instparam_data.data[j++] = colour[i][1]; - this->instparam_data.data[j++] = colour[i][2]; - this->instparam_data.data[j++] = alpha[i]; - this->instparam_data.data[j++] = scale[i]; + + this->push_instparam_data (this->parentVis, colour[i][0]); + this->push_instparam_data (this->parentVis, colour[i][1]); + this->push_instparam_data (this->parentVis, colour[i][2]); + this->push_instparam_data (this->parentVis, alpha[i]); + this->push_instparam_data (this->parentVis, scale[i]); } this->instparam_count = colour.size(); - this->instance_data.copy_to_gpu(); - this->instparam_data.copy_to_gpu(); + // This has to occur once only + // this->instance_data.copy_to_gpu(); + // this->instparam_data.copy_to_gpu(); +#endif } #if 0 @@ -194,8 +195,8 @@ namespace mplot _glfn->BindVertexArray(0); // carefully unbind and rebind mplot::gl::Util::checkError (__FILE__, __LINE__, _glfn); - if (this->flags.test (vm_bools::instanced) && this->instance_data.ready() == false) { - this->init_instance_data(); + if (this->flags.test (vm_bools::instanced)) { + this->init_instance_data (this->parentVis); } /* @@ -504,12 +505,6 @@ namespace mplot //! Get the GladGLContext function pointer std::function*)> get_glfn; - //! Shader Storage Buffer Object for instanced rendering - mplot::gl::ssbo::instance_index, - float, mplot::VisualModelBase::max_instance_floats> instance_data; - mplot::gl::ssbo::instparam_index, - float, mplot::VisualModelBase::max_instparam_floats> instparam_data; - protected: //! A vector of pointers to text models that should be rendered. diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index 8aedc7b4..bdfafed4 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -335,6 +335,7 @@ namespace mplot model->get_gprog = &mplot::VisualBase::get_gprog; model->get_tprog = &mplot::VisualBase::get_tprog; model->get_glfn = &mplot::VisualOwnableMX::get_glfn; + model->init_instance_data = &mplot::VisualOwnableMX::init_instance_data; } //! Add a label _text to the scene at position _toffset. Font features are @@ -387,6 +388,47 @@ namespace mplot return tm->getTextGeometry(); } + static void init_instance_data (mplot::VisualBase* _v) + { + if constexpr (mplot::gl::version::has_ssbo (glver) == true) { + if (_v->instance_data.ready() == false) { _v->instance_data.init (_v->glfn); } + if (_v->instparam_data.ready() == false) { _v->instparam_data.init (_v->glfn); } + } else { + throw std::runtime_error ("Instanced rendering requires OpenGL 4.3 or higher"); + } + } +#if 0 + static void resize_instance_data (mplot::VisualBase* _v, std::size_t n_instances) + { + if (this->instance_data.ready()) { + // Or rather, n_instances + n_already_reserved + this->instance_data.data.resize (n_instances * mplot::VisualBase::floats_per_instance); + } + cur_instance_ptr = 0; + if (this->instparam_data.ready()) { + this->instparam_data.data.resize (n_instances * mplot::VisualBase::floats_per_instparam); + } + cur_instparam_ptr = 0; + } + + static void push_instance_data (mplot::VisualBase* _v, const float datum) + { + _v->instance_data.data[cur_instance_ptr++] = datum; + } + + static void push_instparam_data (mplot::VisualBase* _v, const float datum) + { + _v->instparam_data.data[cur_instparam_ptr++] = datum; + } +#endif + //! Shader Storage Buffer Object for instanced rendering + std::size_t cur_instance_ptr = 0; + mplot::gl::ssbo::instance_index, + float, mplot::VisualBase::max_instance_floats> instance_data; + std::size_t cur_instparam_ptr = 0; + mplot::gl::ssbo::instparam_index, + float, mplot::VisualBase::max_instparam_floats> instparam_data; + protected: // Initialize OpenGL shaders, set some flags (Alpha, Anti-aliasing), read in any external // state from json, and set up the coordinate arrows and any VisualTextModels that will be From 8e8620a94ab5c86d6fc4e1808caff9fed592d3c3 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 10:04:11 +0000 Subject: [PATCH 02/15] Compiles now, but I want ssbos to be program-wide objects, so they should got to VisualResources --- mplot/VisualModelImplMX.h | 2 +- mplot/VisualOwnableMX.h | 5 +++-- mplot/VisualTextModelBase.h | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mplot/VisualModelImplMX.h b/mplot/VisualModelImplMX.h index d894dc5d..3acf8990 100644 --- a/mplot/VisualModelImplMX.h +++ b/mplot/VisualModelImplMX.h @@ -196,7 +196,7 @@ namespace mplot mplot::gl::Util::checkError (__FILE__, __LINE__, _glfn); if (this->flags.test (vm_bools::instanced)) { - this->init_instance_data (this->parentVis); + if (this->init_instance_data) { this->init_instance_data (this->parentVis); } } /* diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index bdfafed4..c05d5dc5 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -390,9 +390,10 @@ namespace mplot static void init_instance_data (mplot::VisualBase* _v) { + auto __v = reinterpret_cast*>(_v); if constexpr (mplot::gl::version::has_ssbo (glver) == true) { - if (_v->instance_data.ready() == false) { _v->instance_data.init (_v->glfn); } - if (_v->instparam_data.ready() == false) { _v->instparam_data.init (_v->glfn); } + if (__v->instance_data.ready() == false) { __v->instance_data.init (__v->glfn); } + if (__v->instparam_data.ready() == false) { __v->instparam_data.init (__v->glfn); } } else { throw std::runtime_error ("Instanced rendering requires OpenGL 4.3 or higher"); } diff --git a/mplot/VisualTextModelBase.h b/mplot/VisualTextModelBase.h index da547f23..fa15524f 100644 --- a/mplot/VisualTextModelBase.h +++ b/mplot/VisualTextModelBase.h @@ -222,6 +222,9 @@ namespace mplot //! Release OpenGL context. Should call parentVis->releaseContext(). std::function*)> releaseContext; + //! Unused, but have to be present + std::function*)> init_instance_data; + //! Setter for the parent pointer, parentVis void set_parent (mplot::VisualBase* _vis) { From 8a18ce4d1b8e1d827871722d6b9494a99c8f2d96 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 10:07:46 +0000 Subject: [PATCH 03/15] Diff simplification --- mplot/VisualModelImplMX.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/mplot/VisualModelImplMX.h b/mplot/VisualModelImplMX.h index 3acf8990..f8781fc8 100644 --- a/mplot/VisualModelImplMX.h +++ b/mplot/VisualModelImplMX.h @@ -102,16 +102,11 @@ namespace mplot size_t j = 0; resize_instance_data (this->parentVis, position.size()); // callback to parent. - //this->instance_data.data.resize (mplot::VisualBase::floats_per_instance * position.size()); - for (size_t i = 0; i < position.size(); ++i) { sm::vec p = position[i]; this->push_instance_data (this->parentVis, p[0]); this->push_instance_data (this->parentVis, p[1]); this->push_instance_data (this->parentVis, p[2]); - //this->instance_data.data[j++] = p[0]; - //this->instance_data.data[j++] = p[1]; - //this->instance_data.data[j++] = p[2]; } this->instance_count = position.size(); @@ -119,8 +114,7 @@ namespace mplot throw std::runtime_error ("set_instance_data: params vvecs should all have same size (colour, rotn, scale)"); } - // resize_instance_data also does this: - //this->instparam_data.data.resize (mplot::VisualBase::floats_per_instparam * colour.size()); j = 0; + // resize_instance_data also resizes instparam_data for (size_t i = 0; i < colour.size(); ++i) { this->push_instparam_data (this->parentVis, colour[i][0]); @@ -131,7 +125,7 @@ namespace mplot } this->instparam_count = colour.size(); - // This has to occur once only + // This has to occur once only, in the right place (FIXME) // this->instance_data.copy_to_gpu(); // this->instparam_data.copy_to_gpu(); #endif From 505e08d48ceda668bec5fab5c73a29e70570a323 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 10:08:32 +0000 Subject: [PATCH 04/15] Diff simplification --- mplot/VisualModelImplMX.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/mplot/VisualModelImplMX.h b/mplot/VisualModelImplMX.h index f8781fc8..489e0527 100644 --- a/mplot/VisualModelImplMX.h +++ b/mplot/VisualModelImplMX.h @@ -99,9 +99,7 @@ namespace mplot << colour.size() << ", " << alpha.size() << ", " << scale.size() << std::endl; #if 0 - size_t j = 0; resize_instance_data (this->parentVis, position.size()); // callback to parent. - for (size_t i = 0; i < position.size(); ++i) { sm::vec p = position[i]; this->push_instance_data (this->parentVis, p[0]); @@ -116,7 +114,6 @@ namespace mplot // resize_instance_data also resizes instparam_data for (size_t i = 0; i < colour.size(); ++i) { - this->push_instparam_data (this->parentVis, colour[i][0]); this->push_instparam_data (this->parentVis, colour[i][1]); this->push_instparam_data (this->parentVis, colour[i][2]); From 0df278316e5207b874b5eacfb10953f93703804d Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 10:09:32 +0000 Subject: [PATCH 05/15] Diff simplification --- mplot/VisualModelImplMX.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mplot/VisualModelImplMX.h b/mplot/VisualModelImplMX.h index 489e0527..10328bcd 100644 --- a/mplot/VisualModelImplMX.h +++ b/mplot/VisualModelImplMX.h @@ -186,8 +186,8 @@ namespace mplot _glfn->BindVertexArray(0); // carefully unbind and rebind mplot::gl::Util::checkError (__FILE__, __LINE__, _glfn); - if (this->flags.test (vm_bools::instanced)) { - if (this->init_instance_data) { this->init_instance_data (this->parentVis); } + if (this->flags.test (vm_bools::instanced) && this->init_instance_data) { + this->init_instance_data (this->parentVis); } /* From 5e89f44cf97f4b0fa3d83be3255edb15b0b70e45 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 10:12:05 +0000 Subject: [PATCH 06/15] Moves some code to the 'for future use' ifdef --- mplot/VisualOwnableMX.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index c05d5dc5..538ec784 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -421,12 +421,12 @@ namespace mplot { _v->instparam_data.data[cur_instparam_ptr++] = datum; } + std::size_t cur_instance_ptr = 0; + std::size_t cur_instparam_ptr = 0; #endif //! Shader Storage Buffer Object for instanced rendering - std::size_t cur_instance_ptr = 0; mplot::gl::ssbo::instance_index, float, mplot::VisualBase::max_instance_floats> instance_data; - std::size_t cur_instparam_ptr = 0; mplot::gl::ssbo::instparam_index, float, mplot::VisualBase::max_instparam_floats> instparam_data; From ef1afcc5296a19921ca7f7aa4bc82033e3e2ee7e Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 10:59:24 +0000 Subject: [PATCH 07/15] Moves ssbos into VisualResources --- mplot/VisualBase.h | 18 ------------------ mplot/VisualModelImplMX.h | 1 - mplot/VisualOwnableMX.h | 13 ++----------- mplot/VisualResourcesBase.h | 20 ++++++++++++++++++++ mplot/VisualResourcesMX.h | 23 +++++++++++++++++++++++ 5 files changed, 45 insertions(+), 30 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 2973ce36..2c176cf8 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -381,24 +381,6 @@ namespace mplot //! Strength of the diffuse light source float diffuse_intensity = 0.0f; - //! Instanced rendering mode (SSBO access). position data stored in SSBO index 1 (must match GLSL code) - static constexpr unsigned int instance_index = 1; - //! colour, scale, rotation stored in SSBO index 2 - static constexpr unsigned int instparam_index = 2; - //! one 3D vector is 3 floats - static constexpr unsigned int floats_per_instance = 3; - //! Instance params are: colour/alpha (4 floats), scale (1 float) - static constexpr unsigned int floats_per_instparam = 5; - - //! This will control how much GPU RAM is allocated when using instanced rendering (the RAM - //! is *only* allocated if at least one VisualModel is 'instanced'). - static constexpr unsigned int max_instances = 256 * 1024; - static constexpr unsigned int max_instance_floats = floats_per_instance * max_instances; - static constexpr unsigned int max_instparam_floats = floats_per_instparam * max_instances; - constexpr unsigned int max_instanced_items() { return max_instances; } - - //static void init_instance_data (mplot::VisualBase* _v) = 0; - //! Compute position and rotation of coordinate arrows in the bottom left of the screen void positionCoordArrows() { diff --git a/mplot/VisualModelImplMX.h b/mplot/VisualModelImplMX.h index 10328bcd..091dc472 100644 --- a/mplot/VisualModelImplMX.h +++ b/mplot/VisualModelImplMX.h @@ -20,7 +20,6 @@ #include #include -#include #include #include diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index 538ec784..0ab37c8f 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -391,12 +391,8 @@ namespace mplot static void init_instance_data (mplot::VisualBase* _v) { auto __v = reinterpret_cast*>(_v); - if constexpr (mplot::gl::version::has_ssbo (glver) == true) { - if (__v->instance_data.ready() == false) { __v->instance_data.init (__v->glfn); } - if (__v->instparam_data.ready() == false) { __v->instparam_data.init (__v->glfn); } - } else { - throw std::runtime_error ("Instanced rendering requires OpenGL 4.3 or higher"); - } + mplot::VisualResourcesMX::i().init_instance_data (__v->glfn); + // Also claim a start point/size within the SSBOs? } #if 0 static void resize_instance_data (mplot::VisualBase* _v, std::size_t n_instances) @@ -424,11 +420,6 @@ namespace mplot std::size_t cur_instance_ptr = 0; std::size_t cur_instparam_ptr = 0; #endif - //! Shader Storage Buffer Object for instanced rendering - mplot::gl::ssbo::instance_index, - float, mplot::VisualBase::max_instance_floats> instance_data; - mplot::gl::ssbo::instparam_index, - float, mplot::VisualBase::max_instparam_floats> instparam_data; protected: // Initialize OpenGL shaders, set some flags (Alpha, Anti-aliasing), read in any external diff --git a/mplot/VisualResourcesBase.h b/mplot/VisualResourcesBase.h index 32b986d4..964f9c3a 100644 --- a/mplot/VisualResourcesBase.h +++ b/mplot/VisualResourcesBase.h @@ -73,6 +73,26 @@ namespace mplot // Note: get/clearVisualFace functions are in derived classes virtual void clearVisualFaces (mplot::VisualBase* _vis) = 0; + + /*! + * SSBO management + */ + //! Instanced rendering mode (SSBO access). position data stored in SSBO index 1 (must match GLSL code) + static constexpr unsigned int instance_index = 1; + //! colour, scale, rotation stored in SSBO index 2 + static constexpr unsigned int instparam_index = 2; + //! one 3D vector is 3 floats + static constexpr unsigned int floats_per_instance = 3; + //! Instance params are: colour/alpha (4 floats), scale (1 float) + static constexpr unsigned int floats_per_instparam = 5; + + //! This will control how much GPU RAM is allocated when using instanced rendering + //! (Hopefully, when I'm finished, the RAM will be allocated only if at least one + //! VisualModel is marked 'instanced'). + static constexpr unsigned int max_instances = 256 * 1024; + static constexpr unsigned int max_instance_floats = floats_per_instance * max_instances; + static constexpr unsigned int max_instparam_floats = floats_per_instparam * max_instances; + //constexpr unsigned int max_instanced_items() { return max_instances; } }; } // namespace mplot diff --git a/mplot/VisualResourcesMX.h b/mplot/VisualResourcesMX.h index a8cf1d7c..4140b7ae 100644 --- a/mplot/VisualResourcesMX.h +++ b/mplot/VisualResourcesMX.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace mplot { @@ -108,6 +109,28 @@ namespace mplot } else { f++; } } } + + /*! + * We also manage some programm-wide SSBO objects for instanced rendering data in + * VisualResources. + */ + void init_instance_data (GladGLContext* glfn) + { + if constexpr (mplot::gl::version::has_ssbo (glver) == true) { + if (this->instance_data.ready() == false) { this->instance_data.init (glfn); } + if (this->instparam_data.ready() == false) { this->instparam_data.init (glfn); } + } else { + throw std::runtime_error ("Instanced rendering requires OpenGL 4.3 or higher"); + } + } + + //! Shader Storage Buffer Object for instanced rendering - this holds positions only + mplot::gl::ssbo::instance_index, + float, mplot::VisualResourcesBase::max_instance_floats> instance_data; + //! Shader Storage Buffer Object for instanced rendering - this holds colour, alpha and scale + mplot::gl::ssbo::instparam_index, + float, mplot::VisualResourcesBase::max_instparam_floats> instparam_data; + }; } // namespace mplot From f1339c6e8fa6963709b1e34952ee7e82f7dc97df Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 13:24:00 +0000 Subject: [PATCH 08/15] Am now allocating and then inserting data into SSBOs --- examples/breadcrumbs.cpp | 4 ++- mplot/VisualMX.h | 7 ++-- mplot/VisualModelBase.h | 17 +++++++--- mplot/VisualModelImplMX.h | 64 +++++++++++++++---------------------- mplot/VisualOwnableMX.h | 36 ++++++--------------- mplot/VisualResourcesBase.h | 4 ++- mplot/VisualResourcesMX.h | 24 ++++++++++++-- mplot/VisualTextModelBase.h | 8 +++-- 8 files changed, 85 insertions(+), 79 deletions(-) diff --git a/examples/breadcrumbs.cpp b/examples/breadcrumbs.cpp index bf1e6572..a9469b02 100644 --- a/examples/breadcrumbs.cpp +++ b/examples/breadcrumbs.cpp @@ -41,6 +41,7 @@ int main() // A normal, non instanced model. A sphere to orbit around. auto gv1 = std::make_unique> (sm::vec<>{}, 0.2f); + gv1->name = "geodesic"; v.bindmodel (gv1); gv1->iterations = 3; gv1->cm.setType (mplot::ColourMapType::Tofino); @@ -65,6 +66,7 @@ int main() } auto isv = std::make_unique> (sm::vec<>{}); + isv->name = "isv1"; v.bindmodel (isv); isv->radiusFixed = 0.03f; isv->finalize(); @@ -72,6 +74,7 @@ int main() // Another one isv = std::make_unique> (sm::vec<>{0,1,0}); + isv->name = "isv2"; v.bindmodel (isv); isv->radiusFixed = 0.03f; isv->finalize(); @@ -79,7 +82,6 @@ int main() v.render(); isvp->set_instance_data (points); // colour, alpha, scale - std::cout << "isvp->instance_count = " << isvp->instance_count << std::endl; isvp2->set_instance_data (points * 1.2f, mplot::colour::black, 0.7f, 1.0f); diff --git a/mplot/VisualMX.h b/mplot/VisualMX.h index bd9b9da9..1b941970 100644 --- a/mplot/VisualMX.h +++ b/mplot/VisualMX.h @@ -165,17 +165,18 @@ namespace mplot void bindmodel (std::unique_ptr& model) { mplot::VisualBase::template bindmodel (model); // base class binds - model->setContext = &mplot::VisualBase::set_context; - model->releaseContext = &mplot::VisualBase::release_context; - model->get_glfn = &mplot::VisualOwnableMX::get_glfn; + this->bindextra (model); } + // The GL-dependent binds template void bindextra (std::unique_ptr& model) { model->setContext = &mplot::VisualBase::set_context; model->releaseContext = &mplot::VisualBase::release_context; model->get_glfn = &mplot::VisualOwnableMX::get_glfn; + model->init_instance_data = &mplot::VisualOwnableMX::init_instance_data; + model->insert_instance_data = &mplot::VisualOwnableMX::insert_instance_data; } /* diff --git a/mplot/VisualModelBase.h b/mplot/VisualModelBase.h index 8989cdf9..2e5cb40e 100644 --- a/mplot/VisualModelBase.h +++ b/mplot/VisualModelBase.h @@ -718,10 +718,19 @@ namespace mplot GLuint idx = 0u; GLuint idx_bb = 0u; - //! If drawing with instancing, how many instances? - unsigned int instance_count = 0; + /*! + * This is the upper limit for instance_count. We reserve max_isntances of space in the SSBO. + * + * Max number of instances in a model. Each *model* has to reserve space in the SSBOs which + * are managed by VisualResources. VisualResources::max_instances must be larger than this. + */ + unsigned int max_instances = 16 * 1024; + //! The offset in the SSBO for the instances for this VisualModel + unsigned int instance_offset = 0; //! Which datum in the instance buffer is the one to be drawn by the first thread? unsigned int instance_start = 0; + //! If drawing with instancing, how many instances? + unsigned int instance_count = 0; //! If drawing with instancing, how many params will be used (these will be cycled through //! per-instance and there may be fewer than instance_count parameters) unsigned int instparam_count = 0; @@ -743,8 +752,8 @@ namespace mplot std::function*)> releaseContext; //! Init the SSBOs for instanced rendering - std::function*)> init_instance_data; - std::function*, std::size_t)> resize_instance_data; + std::function*, const unsigned int)> init_instance_data; + std::function&)> insert_instance_data; //! Set up the instance positions (with default params for colour, rotn, scale) virtual void set_instance_data (const sm::vvec>& position) = 0; diff --git a/mplot/VisualModelImplMX.h b/mplot/VisualModelImplMX.h index 091dc472..46b034bb 100644 --- a/mplot/VisualModelImplMX.h +++ b/mplot/VisualModelImplMX.h @@ -94,30 +94,34 @@ namespace mplot { if (position.size() < 1) { throw std::runtime_error ("set_instance_data: pass some instance positions in"); } //if (position.size() > this->max_instanced_items()) { throw std::runtime_error ("set_instance_data: not enough space"); } - std::cout << "Colour, alpha, scale sizes: " - << colour.size() << ", " << alpha.size() << ", " << scale.size() << std::endl; -#if 0 - resize_instance_data (this->parentVis, position.size()); // callback to parent. - for (size_t i = 0; i < position.size(); ++i) { - sm::vec p = position[i]; - this->push_instance_data (this->parentVis, p[0]); - this->push_instance_data (this->parentVis, p[1]); - this->push_instance_data (this->parentVis, p[2]); + if (position.size() > this->max_instances) { + throw std::runtime_error ("set_instance_data: Haven't reserved enough space for that"); + } + //if (!this->insert_instance_data) { + // throw std::runtime_error ("set_instance_data: Haven't connected insert_instance_data"); + //} + + if (this->insert_instance_data) { + for (size_t i = 0; i < position.size(); ++i) { + // Get access to the SSBO in VisualResources and add the 3 floats in position[i] at + // the location defined by this->instance_offset + i + this->insert_instance_data (this->instance_offset + i, position[i]); + } + } else { + std::cout << "No insert instance data?" << std::endl; } this->instance_count = position.size(); if (colour.size() != scale.size() || colour.size() != alpha.size()) { throw std::runtime_error ("set_instance_data: params vvecs should all have same size (colour, rotn, scale)"); } - +#if 0 // resize_instance_data also resizes instparam_data for (size_t i = 0; i < colour.size(); ++i) { - this->push_instparam_data (this->parentVis, colour[i][0]); - this->push_instparam_data (this->parentVis, colour[i][1]); - this->push_instparam_data (this->parentVis, colour[i][2]); - this->push_instparam_data (this->parentVis, alpha[i]); - this->push_instparam_data (this->parentVis, scale[i]); + this->push_instparam_data (this->parentVis, colour, alpha, scale); + //this->push_instparam_data (this->parentVis, alpha[i]); + //this->push_instparam_data (this->parentVis, scale[i]); } this->instparam_count = colour.size(); @@ -127,29 +131,6 @@ namespace mplot #endif } -#if 0 - // Update the instance_data from points and data. Update the range of data starting at - // points/data index i_s and ending at i_e. - void update_instance_data (const sm::vvec>& points, const sm::vvec& data, - std::size_t i_s, std::size_t i_e) - { - if (points.size() != data.size()) { throw std::runtime_error ("points and data must have same size"); } - if (points.size() > this->max_instanced_items()) { throw std::runtime_error ("Not enough space"); } - - sm::vvec updated(mplot::VisualModelBase::floats_per_instance * (1 + i_e - i_s)); - size_t j = 0; - for (size_t i = i_s; i <= i_e; ++i) { - //std::cout << "writing points/data["< c = points[i]; - updated[j++] = c[0]; - updated[j++] = c[1]; - updated[j++] = c[2]; - } - //std::cout << "Copy updated, size " << updated.size() << " to gpu with offset " << i_s << "\n"; - this->instance_data.copy_to_gpu (updated, i_s); - } -#endif - //! Common code to call after the vertices have been set up. GL has to have been initialised. void postVertexInit() final { @@ -186,7 +167,12 @@ namespace mplot mplot::gl::Util::checkError (__FILE__, __LINE__, _glfn); if (this->flags.test (vm_bools::instanced) && this->init_instance_data) { - this->init_instance_data (this->parentVis); + // Here, we cause the SSBOs to be intialized if they haven't already, and we reserve + // some space in the SSBOs for *this model* + this->instance_offset = this->init_instance_data (this->parentVis, this->max_instances); + if (this->instance_offset == std::numeric_limits::max()) { + throw std::runtime_error ("Failed to reserve space in SSBO"); + } } /* diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index 0ab37c8f..d2cd3e92 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -327,6 +327,7 @@ namespace mplot } } + // Note: We have to have both VisualOwnableMX::bindmodel AND VisualMX::bindmodel (which calls VisualBase::bindmodel) template void bindmodel (std::unique_ptr& model) { @@ -336,6 +337,7 @@ namespace mplot model->get_tprog = &mplot::VisualBase::get_tprog; model->get_glfn = &mplot::VisualOwnableMX::get_glfn; model->init_instance_data = &mplot::VisualOwnableMX::init_instance_data; + model->insert_instance_data = &mplot::VisualOwnableMX::insert_instance_data; } //! Add a label _text to the scene at position _toffset. Font features are @@ -388,38 +390,17 @@ namespace mplot return tm->getTextGeometry(); } - static void init_instance_data (mplot::VisualBase* _v) + static unsigned int init_instance_data (mplot::VisualBase* _v, const unsigned int n_to_reserve) { auto __v = reinterpret_cast*>(_v); - mplot::VisualResourcesMX::i().init_instance_data (__v->glfn); - // Also claim a start point/size within the SSBOs? - } -#if 0 - static void resize_instance_data (mplot::VisualBase* _v, std::size_t n_instances) - { - if (this->instance_data.ready()) { - // Or rather, n_instances + n_already_reserved - this->instance_data.data.resize (n_instances * mplot::VisualBase::floats_per_instance); - } - cur_instance_ptr = 0; - if (this->instparam_data.ready()) { - this->instparam_data.data.resize (n_instances * mplot::VisualBase::floats_per_instparam); - } - cur_instparam_ptr = 0; - } - - static void push_instance_data (mplot::VisualBase* _v, const float datum) - { - _v->instance_data.data[cur_instance_ptr++] = datum; + unsigned int reservation = mplot::VisualResourcesMX::i().init_instance_ssbo (__v->glfn, n_to_reserve); + return reservation; } - static void push_instparam_data (mplot::VisualBase* _v, const float datum) + static void insert_instance_data (const unsigned int instance_idx, const sm::vec& coord) { - _v->instparam_data.data[cur_instparam_ptr++] = datum; + mplot::VisualResourcesMX::i().insert_instance_data (instance_idx, coord); } - std::size_t cur_instance_ptr = 0; - std::size_t cur_instparam_ptr = 0; -#endif protected: // Initialize OpenGL shaders, set some flags (Alpha, Anti-aliasing), read in any external @@ -470,6 +451,7 @@ namespace mplot // Use coordArrowsOffset to set the location of the CoordArrows *scene* this->coordArrows = std::make_unique>(); + this->coordArrows->name = "Scene axes"; // For CoordArrows, because we don't add via Visual::addVisualModel(), we // have to set the get_shaderprogs function here: this->bindmodel (this->coordArrows); @@ -480,6 +462,7 @@ namespace mplot // Create 'user frame of reference object' this->userFrame = std::make_unique>(); + this->userFrame->name = "User frame of ref object"; this->bindmodel (this->userFrame); this->userFrame->init (sm::vec{}, sm::vec{0.0f, 0.0f, -100.0f}, sm::vec{0.1f, 0.1f, 1.0f}, 0.05f, @@ -494,6 +477,7 @@ namespace mplot // Set up the title, which may or may not be rendered mplot::TextFeatures title_tf(0.035f, 64); this->textModel = std::make_unique> (title_tf); + this->textModel->name = "Title text"; this->bindmodel (this->textModel); this->textModel->setSceneTranslation ({0.0f, 0.0f, 0.0f}); this->textModel->setupText (this->title); diff --git a/mplot/VisualResourcesBase.h b/mplot/VisualResourcesBase.h index 964f9c3a..678d391f 100644 --- a/mplot/VisualResourcesBase.h +++ b/mplot/VisualResourcesBase.h @@ -92,7 +92,9 @@ namespace mplot static constexpr unsigned int max_instances = 256 * 1024; static constexpr unsigned int max_instance_floats = floats_per_instance * max_instances; static constexpr unsigned int max_instparam_floats = floats_per_instparam * max_instances; - //constexpr unsigned int max_instanced_items() { return max_instances; } + + // The Current location from which space in the instance SSBOs should be allocated + unsigned int instance_top = 0; }; } // namespace mplot diff --git a/mplot/VisualResourcesMX.h b/mplot/VisualResourcesMX.h index 4140b7ae..b60692ce 100644 --- a/mplot/VisualResourcesMX.h +++ b/mplot/VisualResourcesMX.h @@ -111,17 +111,35 @@ namespace mplot } /*! - * We also manage some programm-wide SSBO objects for instanced rendering data in - * VisualResources. + * We also manage some programm-wide SSBO objects for instanced rendering + * VisualResourcesdata in . Reserve n_to_reserve instances of data in the SSBOs. Return the + * start offset into the buffers in terms of number of instances */ - void init_instance_data (GladGLContext* glfn) + unsigned int init_instance_ssbo (GladGLContext* glfn, const unsigned int n_to_reserve) { + unsigned int reservation = std::numeric_limits::max(); if constexpr (mplot::gl::version::has_ssbo (glver) == true) { if (this->instance_data.ready() == false) { this->instance_data.init (glfn); } if (this->instparam_data.ready() == false) { this->instparam_data.init (glfn); } + if (n_to_reserve + this->instance_top <= this->max_instances) { + reservation = this->instance_top; + this->instance_top += n_to_reserve; + } } else { throw std::runtime_error ("Instanced rendering requires OpenGL 4.3 or higher"); } + return reservation; + } + + void insert_instance_data (const unsigned int instance_idx, const sm::vec& coord) + { + if (instance_idx >= this->max_instances) { + throw std::runtime_error ("insert_instance_data: bad instance_idx"); + } + unsigned int cur_fidx = instance_idx * this->floats_per_instance; + this->instance_data.data[cur_fidx++] = coord[0]; + this->instance_data.data[cur_fidx++] = coord[1]; + this->instance_data.data[cur_fidx++] = coord[2]; } //! Shader Storage Buffer Object for instanced rendering - this holds positions only diff --git a/mplot/VisualTextModelBase.h b/mplot/VisualTextModelBase.h index fa15524f..6456288c 100644 --- a/mplot/VisualTextModelBase.h +++ b/mplot/VisualTextModelBase.h @@ -201,6 +201,9 @@ namespace mplot virtual void postVertexInit() = 0; public: + // A VisualTextModel may be given a name + std::string name = "VisualTextModel"; + //! The colour of the text std::array clr_text = {0.0f, 0.0f, 0.0f}; //! Line spacing, in multiples of the height of an 'h' @@ -222,8 +225,9 @@ namespace mplot //! Release OpenGL context. Should call parentVis->releaseContext(). std::function*)> releaseContext; - //! Unused, but have to be present - std::function*)> init_instance_data; + //! SSBOs are unused in VisualTextModels, but these functions have to be present + std::function*, const unsigned int)> init_instance_data; + std::function&)> insert_instance_data; //! Setter for the parent pointer, parentVis void set_parent (mplot::VisualBase* _vis) From e8cad3364999000840ebb4fa93d7b11218ed6b5b Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 13:37:35 +0000 Subject: [PATCH 09/15] INserts instparam data --- mplot/VisualMX.h | 1 + mplot/VisualModelBase.h | 1 + mplot/VisualModelImplMX.h | 44 +++++++++++++++++-------------------- mplot/VisualOwnableMX.h | 7 ++++++ mplot/VisualResourcesMX.h | 14 ++++++++++++ mplot/VisualTextModelBase.h | 1 + 6 files changed, 44 insertions(+), 24 deletions(-) diff --git a/mplot/VisualMX.h b/mplot/VisualMX.h index 1b941970..9d3cc2b5 100644 --- a/mplot/VisualMX.h +++ b/mplot/VisualMX.h @@ -177,6 +177,7 @@ namespace mplot model->get_glfn = &mplot::VisualOwnableMX::get_glfn; model->init_instance_data = &mplot::VisualOwnableMX::init_instance_data; model->insert_instance_data = &mplot::VisualOwnableMX::insert_instance_data; + model->insert_instparam_data = &mplot::VisualOwnableMX::insert_instparam_data; } /* diff --git a/mplot/VisualModelBase.h b/mplot/VisualModelBase.h index 2e5cb40e..6c395cf6 100644 --- a/mplot/VisualModelBase.h +++ b/mplot/VisualModelBase.h @@ -754,6 +754,7 @@ namespace mplot //! Init the SSBOs for instanced rendering std::function*, const unsigned int)> init_instance_data; std::function&)> insert_instance_data; + std::function&, const float, const float)> insert_instparam_data; //! Set up the instance positions (with default params for colour, rotn, scale) virtual void set_instance_data (const sm::vvec>& position) = 0; diff --git a/mplot/VisualModelImplMX.h b/mplot/VisualModelImplMX.h index 46b034bb..a46aaab3 100644 --- a/mplot/VisualModelImplMX.h +++ b/mplot/VisualModelImplMX.h @@ -92,43 +92,39 @@ namespace mplot const sm::vvec>& colour, const sm::vvec& alpha, const sm::vvec& scale) final { - if (position.size() < 1) { throw std::runtime_error ("set_instance_data: pass some instance positions in"); } - //if (position.size() > this->max_instanced_items()) { throw std::runtime_error ("set_instance_data: not enough space"); } - + if (position.size() < 1) { + throw std::runtime_error ("set_instance_data: pass some instance positions in"); + } if (position.size() > this->max_instances) { throw std::runtime_error ("set_instance_data: Haven't reserved enough space for that"); } - //if (!this->insert_instance_data) { - // throw std::runtime_error ("set_instance_data: Haven't connected insert_instance_data"); - //} - - if (this->insert_instance_data) { - for (size_t i = 0; i < position.size(); ++i) { - // Get access to the SSBO in VisualResources and add the 3 floats in position[i] at - // the location defined by this->instance_offset + i - this->insert_instance_data (this->instance_offset + i, position[i]); - } - } else { - std::cout << "No insert instance data?" << std::endl; + if (!this->insert_instance_data) { + throw std::runtime_error ("set_instance_data: Function insert_instance_data is not bound"); + } + if (!this->insert_instparam_data) { + throw std::runtime_error ("set_instance_data: Function insert_instparam_data is not bound"); } - this->instance_count = position.size(); - if (colour.size() != scale.size() || colour.size() != alpha.size()) { throw std::runtime_error ("set_instance_data: params vvecs should all have same size (colour, rotn, scale)"); } -#if 0 - // resize_instance_data also resizes instparam_data + + for (size_t i = 0; i < position.size(); ++i) { + // Get access to the SSBO in VisualResources and add the 3 floats in position[i] at + // the location defined by this->instance_offset + i + this->insert_instance_data (this->instance_offset + i, position[i]); + } + + this->instance_count = position.size(); + + for (size_t i = 0; i < colour.size(); ++i) { - this->push_instparam_data (this->parentVis, colour, alpha, scale); - //this->push_instparam_data (this->parentVis, alpha[i]); - //this->push_instparam_data (this->parentVis, scale[i]); + this->insert_instparam_data (this->instance_offset + i, colour[i], alpha[i], scale[i]); } this->instparam_count = colour.size(); - // This has to occur once only, in the right place (FIXME) + // This has to occur once only, in the right place (FIXME). (In Visual::render) // this->instance_data.copy_to_gpu(); // this->instparam_data.copy_to_gpu(); -#endif } //! Common code to call after the vertices have been set up. GL has to have been initialised. diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index d2cd3e92..072c37c6 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -338,6 +338,7 @@ namespace mplot model->get_glfn = &mplot::VisualOwnableMX::get_glfn; model->init_instance_data = &mplot::VisualOwnableMX::init_instance_data; model->insert_instance_data = &mplot::VisualOwnableMX::insert_instance_data; + model->insert_instparam_data = &mplot::VisualOwnableMX::insert_instparam_data; } //! Add a label _text to the scene at position _toffset. Font features are @@ -402,6 +403,12 @@ namespace mplot mplot::VisualResourcesMX::i().insert_instance_data (instance_idx, coord); } + static void insert_instparam_data (const unsigned int instance_idx, + const std::array& colour, const float& alpha, const float& scale) + { + mplot::VisualResourcesMX::i().insert_instparam_data (instance_idx, colour, alpha, scale); + } + protected: // Initialize OpenGL shaders, set some flags (Alpha, Anti-aliasing), read in any external // state from json, and set up the coordinate arrows and any VisualTextModels that will be diff --git a/mplot/VisualResourcesMX.h b/mplot/VisualResourcesMX.h index b60692ce..35ff6940 100644 --- a/mplot/VisualResourcesMX.h +++ b/mplot/VisualResourcesMX.h @@ -142,6 +142,20 @@ namespace mplot this->instance_data.data[cur_fidx++] = coord[2]; } + void insert_instparam_data (const unsigned int instance_idx, + const std::array& colour, const float& alpha, const float& scale) + { + if (instance_idx >= this->max_instances) { + throw std::runtime_error ("insert_instparam_data: bad instance_idx"); + } + unsigned int cur_fidx = instance_idx * this->floats_per_instparam; + this->instparam_data.data[cur_fidx++] = colour[0]; + this->instparam_data.data[cur_fidx++] = colour[1]; + this->instparam_data.data[cur_fidx++] = colour[2]; + this->instparam_data.data[cur_fidx++] = alpha; + this->instparam_data.data[cur_fidx++] = scale; + } + //! Shader Storage Buffer Object for instanced rendering - this holds positions only mplot::gl::ssbo::instance_index, float, mplot::VisualResourcesBase::max_instance_floats> instance_data; diff --git a/mplot/VisualTextModelBase.h b/mplot/VisualTextModelBase.h index 6456288c..ab01ea54 100644 --- a/mplot/VisualTextModelBase.h +++ b/mplot/VisualTextModelBase.h @@ -228,6 +228,7 @@ namespace mplot //! SSBOs are unused in VisualTextModels, but these functions have to be present std::function*, const unsigned int)> init_instance_data; std::function&)> insert_instance_data; + std::function&, const float, const float)> insert_instparam_data; //! Setter for the parent pointer, parentVis void set_parent (mplot::VisualBase* _vis) From b53dc412a4f8a49dbdd4ca26de7a26e3fb79ae07 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 14:23:02 +0000 Subject: [PATCH 10/15] More or less reinstates the working instanced model --- mplot/VisualBase.h | 9 ++++++++- mplot/VisualModelImplMX.h | 6 ------ mplot/VisualOwnableMX.h | 7 +++++++ mplot/VisualResourcesMX.h | 8 ++++++++ mplot/gl/ssbo_mx.h | 6 ++++++ 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 2c176cf8..5be3f983 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -61,7 +61,9 @@ namespace mplot //! When true, cursor movements induce translation of scene translateMode, //! We are scrolling (and so we will need to zero scenetrans_delta after enacting the change) - scrolling + scrolling, + //! True means that at least one of our VisualModels is an instanced rendering model + haveInstanced }; //! Boolean options - similar to state, but more likely to be modified by client code @@ -248,6 +250,7 @@ namespace mplot unsigned int addVisualModelId (std::unique_ptr& model) { std::unique_ptr> vmp = std::move(model); + if (vmp->instanced()) { this->state.set (visual_state::haveInstanced, true); } this->vm.push_back (std::move(vmp)); unsigned int rtn = (this->vm.size()-1); return rtn; @@ -260,6 +263,7 @@ namespace mplot T* addVisualModel (std::unique_ptr& model) { std::unique_ptr> vmp = std::move(model); + if (vmp->instanced()) { this->state.set (visual_state::haveInstanced, true); } this->vm.push_back (std::move(vmp)); return static_cast(this->vm.back().get()); } @@ -461,6 +465,9 @@ namespace mplot //! Returns true if we are in the paused state bool paused() const { return this->state.test (visual_state::paused); } + //! True if one of our added VisualModels is an instanced model + bool haveInstanced() const { return this->state.test (visual_state::haveInstanced); } + /* * User-settable projection values for the near clipping distance, the far clipping distance * and the field of view of the camera. diff --git a/mplot/VisualModelImplMX.h b/mplot/VisualModelImplMX.h index a46aaab3..d51c23c9 100644 --- a/mplot/VisualModelImplMX.h +++ b/mplot/VisualModelImplMX.h @@ -113,18 +113,12 @@ namespace mplot // the location defined by this->instance_offset + i this->insert_instance_data (this->instance_offset + i, position[i]); } - this->instance_count = position.size(); - for (size_t i = 0; i < colour.size(); ++i) { this->insert_instparam_data (this->instance_offset + i, colour[i], alpha[i], scale[i]); } this->instparam_count = colour.size(); - - // This has to occur once only, in the right place (FIXME). (In Visual::render) - // this->instance_data.copy_to_gpu(); - // this->instparam_data.copy_to_gpu(); } //! Common code to call after the vertices have been set up. GL has to have been initialised. diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index 072c37c6..d64c59c3 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -259,6 +259,8 @@ namespace mplot this->userFrame->render(); } + if (this->haveInstanced()) { this->copy_instance_data_to_gpu(); } + auto vmi = this->vm.begin(); while (vmi != this->vm.end()) { if ((*vmi)->twodimensional() == true) { @@ -409,6 +411,11 @@ namespace mplot mplot::VisualResourcesMX::i().insert_instparam_data (instance_idx, colour, alpha, scale); } + static void copy_instance_data_to_gpu() + { + mplot::VisualResourcesMX::i().copy_instance_ssbo_to_gpu(); + } + protected: // Initialize OpenGL shaders, set some flags (Alpha, Anti-aliasing), read in any external // state from json, and set up the coordinate arrows and any VisualTextModels that will be diff --git a/mplot/VisualResourcesMX.h b/mplot/VisualResourcesMX.h index 35ff6940..8fd67d34 100644 --- a/mplot/VisualResourcesMX.h +++ b/mplot/VisualResourcesMX.h @@ -124,6 +124,8 @@ namespace mplot if (n_to_reserve + this->instance_top <= this->max_instances) { reservation = this->instance_top; this->instance_top += n_to_reserve; + this->instance_data.resize (this->instance_top * this->floats_per_instance); + this->instparam_data.resize (this->instance_top * this->floats_per_instparam); } } else { throw std::runtime_error ("Instanced rendering requires OpenGL 4.3 or higher"); @@ -156,6 +158,12 @@ namespace mplot this->instparam_data.data[cur_fidx++] = scale; } + void copy_instance_ssbo_to_gpu() + { + if (this->instance_data.ready()) { this->instance_data.copy_to_gpu(); } + if (this->instparam_data.ready()) { this->instparam_data.copy_to_gpu(); } + } + //! Shader Storage Buffer Object for instanced rendering - this holds positions only mplot::gl::ssbo::instance_index, float, mplot::VisualResourcesBase::max_instance_floats> instance_data; diff --git a/mplot/gl/ssbo_mx.h b/mplot/gl/ssbo_mx.h index d45503dc..7544723a 100644 --- a/mplot/gl/ssbo_mx.h +++ b/mplot/gl/ssbo_mx.h @@ -33,6 +33,12 @@ namespace mplot::gl bool ready() const { return this->name != 0u; } + void resize (std::size_t sz) + { + if (sz > N) { throw std::runtime_error ("ssbo: can't resize to this size"); } + this->data.resize (sz); + } + ssbo() {} ~ssbo() {} From 6b60d739d96235db122ebddd7425dea25fdc1954 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 14:40:53 +0000 Subject: [PATCH 11/15] Copy to GPU only when required --- mplot/VisualBase.h | 11 ++++++++++- mplot/VisualModelBase.h | 1 + mplot/VisualModelImplMX.h | 2 ++ mplot/VisualOwnableMX.h | 6 +++++- mplot/VisualTextModelBase.h | 1 + 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/mplot/VisualBase.h b/mplot/VisualBase.h index 5be3f983..5a73e54d 100644 --- a/mplot/VisualBase.h +++ b/mplot/VisualBase.h @@ -63,7 +63,9 @@ namespace mplot //! We are scrolling (and so we will need to zero scenetrans_delta after enacting the change) scrolling, //! True means that at least one of our VisualModels is an instanced rendering model - haveInstanced + haveInstanced, + //! When true, the instanced data SSBO needs to be copied to the GPU + instancedNeedsUpdate }; //! Boolean options - similar to state, but more likely to be modified by client code @@ -240,6 +242,7 @@ namespace mplot model->get_shaderprogs = &mplot::VisualBase::get_shaderprogs; model->get_gprog = &mplot::VisualBase::get_gprog; model->get_tprog = &mplot::VisualBase::get_tprog; + model->instanced_needs_update = &mplot::VisualBase::instanced_needs_update; } /*! @@ -376,6 +379,8 @@ namespace mplot static GLuint get_gprog (mplot::VisualBase* _v) { return _v->shaders.gprog; }; static GLuint get_tprog (mplot::VisualBase* _v) { return _v->shaders.tprog; }; + static void instanced_needs_update (mplot::VisualBase* _v) { _v->instancedNeedsUpdate (true); } + //! The colour of ambient and diffuse light sources sm::vec light_colour = { 1.0f, 1.0f, 1.0f }; //! Strength of the ambient light @@ -468,6 +473,10 @@ namespace mplot //! True if one of our added VisualModels is an instanced model bool haveInstanced() const { return this->state.test (visual_state::haveInstanced); } + //! Does our instanced data need to be pushed over to the GPU during render()? + bool instancedNeedsUpdate() const { return this->state.test (visual_state::instancedNeedsUpdate); } + void instancedNeedsUpdate (const bool val) { this->state.set (visual_state::instancedNeedsUpdate, val); } + /* * User-settable projection values for the near clipping distance, the far clipping distance * and the field of view of the camera. diff --git a/mplot/VisualModelBase.h b/mplot/VisualModelBase.h index 6c395cf6..fffaf3f1 100644 --- a/mplot/VisualModelBase.h +++ b/mplot/VisualModelBase.h @@ -755,6 +755,7 @@ namespace mplot std::function*, const unsigned int)> init_instance_data; std::function&)> insert_instance_data; std::function&, const float, const float)> insert_instparam_data; + std::function*)> instanced_needs_update; //! Set up the instance positions (with default params for colour, rotn, scale) virtual void set_instance_data (const sm::vvec>& position) = 0; diff --git a/mplot/VisualModelImplMX.h b/mplot/VisualModelImplMX.h index d51c23c9..e2c6df7e 100644 --- a/mplot/VisualModelImplMX.h +++ b/mplot/VisualModelImplMX.h @@ -119,6 +119,8 @@ namespace mplot this->insert_instparam_data (this->instance_offset + i, colour[i], alpha[i], scale[i]); } this->instparam_count = colour.size(); + + this->instanced_needs_update (this->parentVis); } //! Common code to call after the vertices have been set up. GL has to have been initialised. diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index d64c59c3..cb335fe7 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -259,7 +259,10 @@ namespace mplot this->userFrame->render(); } - if (this->haveInstanced()) { this->copy_instance_data_to_gpu(); } + if (this->haveInstanced() && this->instancedNeedsUpdate()) { + this->copy_instance_data_to_gpu(); + this->instancedNeedsUpdate (false); + } auto vmi = this->vm.begin(); while (vmi != this->vm.end()) { @@ -337,6 +340,7 @@ namespace mplot model->get_shaderprogs = &mplot::VisualBase::get_shaderprogs; model->get_gprog = &mplot::VisualBase::get_gprog; model->get_tprog = &mplot::VisualBase::get_tprog; + model->instanced_needs_update = &mplot::VisualBase::instanced_needs_update; model->get_glfn = &mplot::VisualOwnableMX::get_glfn; model->init_instance_data = &mplot::VisualOwnableMX::init_instance_data; model->insert_instance_data = &mplot::VisualOwnableMX::insert_instance_data; diff --git a/mplot/VisualTextModelBase.h b/mplot/VisualTextModelBase.h index ab01ea54..92b734ad 100644 --- a/mplot/VisualTextModelBase.h +++ b/mplot/VisualTextModelBase.h @@ -229,6 +229,7 @@ namespace mplot std::function*, const unsigned int)> init_instance_data; std::function&)> insert_instance_data; std::function&, const float, const float)> insert_instparam_data; + std::function*)> instanced_needs_update; //! Setter for the parent pointer, parentVis void set_parent (mplot::VisualBase* _vis) From 74c580cb21e0b0d8a58d31e0cc296fcd18b4cf32 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 15:09:10 +0000 Subject: [PATCH 12/15] Updates the shader to use the now-separate SSBO data --- mplot/VisualDefaultShaders.h | 8 +++++--- mplot/VisualModelBase.h | 6 ++---- mplot/VisualModelImplMX.h | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/mplot/VisualDefaultShaders.h b/mplot/VisualDefaultShaders.h index 49a1dfa7..45708c0a 100644 --- a/mplot/VisualDefaultShaders.h +++ b/mplot/VisualDefaultShaders.h @@ -46,14 +46,16 @@ namespace mplot "void main()\n" "{\n" " if (instance_count > 0) {\n" - " vec4 iposv = { ipos[gl_InstanceID * 3], ipos[gl_InstanceID * 3 + 1], ipos[gl_InstanceID * 3 + 2], 0 };\n" + " int ipos_i = instance_start * 3 + gl_InstanceID * 3;\n" + " vec4 iposv = { ipos[ipos_i], ipos[ipos_i + 1], ipos[ipos_i + 2], 0 };\n" " if (instparam_count > 0) {\n" " int idx = gl_InstanceID % instparam_count;\n" - " float s = iparam[idx * 5 + 4] * 0.5f;\n" + " int ippos_i = instance_start * 5;\n" + " float s = iparam[ippos_i + idx * 5 + 4];\n" " vec3 p_three = vec3(position) * s;\n" " vec4 p_scaled = vec4(p_three, 1.0);\n" " gl_Position = (p_matrix * v_matrix * m_matrix * (p_scaled + iposv));\n" - " vertex.color = vec4(iparam[idx * 5], iparam[idx * 5 + 1], iparam[idx * 5 + 2], iparam[idx * 5 + 3]);\n" + " vertex.color = vec4(iparam[ippos_i + idx * 5], iparam[ippos_i + idx * 5 + 1], iparam[ippos_i + idx * 5 + 2], iparam[ippos_i + idx * 5 + 3]);\n" " vertex.fragpos = vec3(m_matrix * p_scaled);\n" " } else {\n" " gl_Position = (p_matrix * v_matrix * m_matrix * (position + iposv));\n" diff --git a/mplot/VisualModelBase.h b/mplot/VisualModelBase.h index fffaf3f1..c2e65d8f 100644 --- a/mplot/VisualModelBase.h +++ b/mplot/VisualModelBase.h @@ -725,11 +725,9 @@ namespace mplot * are managed by VisualResources. VisualResources::max_instances must be larger than this. */ unsigned int max_instances = 16 * 1024; - //! The offset in the SSBO for the instances for this VisualModel - unsigned int instance_offset = 0; - //! Which datum in the instance buffer is the one to be drawn by the first thread? + //! The offset into the SSBOs for the instance data for this VisualModel unsigned int instance_start = 0; - //! If drawing with instancing, how many instances? + //! If drawing with instancing, how many instances? <= this->max_instances unsigned int instance_count = 0; //! If drawing with instancing, how many params will be used (these will be cycled through //! per-instance and there may be fewer than instance_count parameters) diff --git a/mplot/VisualModelImplMX.h b/mplot/VisualModelImplMX.h index e2c6df7e..861b0a4d 100644 --- a/mplot/VisualModelImplMX.h +++ b/mplot/VisualModelImplMX.h @@ -110,13 +110,13 @@ namespace mplot for (size_t i = 0; i < position.size(); ++i) { // Get access to the SSBO in VisualResources and add the 3 floats in position[i] at - // the location defined by this->instance_offset + i - this->insert_instance_data (this->instance_offset + i, position[i]); + // the location defined by this->instance_start + i + this->insert_instance_data (this->instance_start + i, position[i]); } this->instance_count = position.size(); for (size_t i = 0; i < colour.size(); ++i) { - this->insert_instparam_data (this->instance_offset + i, colour[i], alpha[i], scale[i]); + this->insert_instparam_data (this->instance_start + i, colour[i], alpha[i], scale[i]); } this->instparam_count = colour.size(); @@ -161,8 +161,8 @@ namespace mplot if (this->flags.test (vm_bools::instanced) && this->init_instance_data) { // Here, we cause the SSBOs to be intialized if they haven't already, and we reserve // some space in the SSBOs for *this model* - this->instance_offset = this->init_instance_data (this->parentVis, this->max_instances); - if (this->instance_offset == std::numeric_limits::max()) { + this->instance_start = this->init_instance_data (this->parentVis, this->max_instances); + if (this->instance_start == std::numeric_limits::max()) { throw std::runtime_error ("Failed to reserve space in SSBO"); } } From 4143d2b0cb05caba199feeeffbe238b8b953d083 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 15:49:07 +0000 Subject: [PATCH 13/15] Notes and reduction in size of SSBO's allocated --- mplot/VisualResourcesBase.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mplot/VisualResourcesBase.h b/mplot/VisualResourcesBase.h index 678d391f..4f262e5e 100644 --- a/mplot/VisualResourcesBase.h +++ b/mplot/VisualResourcesBase.h @@ -88,8 +88,9 @@ namespace mplot //! This will control how much GPU RAM is allocated when using instanced rendering //! (Hopefully, when I'm finished, the RAM will be allocated only if at least one - //! VisualModel is marked 'instanced'). - static constexpr unsigned int max_instances = 256 * 1024; + //! VisualModel is marked 'instanced'). Makes a big difference to speed of operation (unless + //! I can send a portion of a buffer to the GPU). + static constexpr unsigned int max_instances = 32 * 1024; static constexpr unsigned int max_instance_floats = floats_per_instance * max_instances; static constexpr unsigned int max_instparam_floats = floats_per_instparam * max_instances; From 1a23a331aa96f37b7efb55592dab13e1c704fa69 Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 15:50:20 +0000 Subject: [PATCH 14/15] set max-instances for our instanced models --- examples/breadcrumbs.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/breadcrumbs.cpp b/examples/breadcrumbs.cpp index a9469b02..648da57f 100644 --- a/examples/breadcrumbs.cpp +++ b/examples/breadcrumbs.cpp @@ -68,14 +68,16 @@ int main() auto isv = std::make_unique> (sm::vec<>{}); isv->name = "isv1"; v.bindmodel (isv); + isv->max_instances = dsz; isv->radiusFixed = 0.03f; isv->finalize(); auto isvp = v.addVisualModel (isv); // Another one - isv = std::make_unique> (sm::vec<>{0,1,0}); + isv = std::make_unique> (sm::vec<>{0,0.1,0}); isv->name = "isv2"; v.bindmodel (isv); + isv->max_instances = dsz; isv->radiusFixed = 0.03f; isv->finalize(); auto isvp2 = v.addVisualModel (isv); @@ -103,7 +105,7 @@ int main() isvp->set_instance_data (points, col, alph, scl); v.render(); - v.waitevents (0.03); + v.waitevents (0.018); #else // I haven't mastered updating a subrange of data in an SSBO // update circularly, change isvp->instance_start each time From f1b5979753cb014e82004b7a54fc3892ead7989f Mon Sep 17 00:00:00 2001 From: Seb James Date: Thu, 18 Dec 2025 16:25:59 +0000 Subject: [PATCH 15/15] Applies changes to the NoMX classes --- mplot/VisualModelImplNoMX.h | 66 +++++++++++++++---------------------- mplot/VisualNoMX.h | 7 ++-- mplot/VisualOwnableMX.h | 3 -- mplot/VisualOwnableNoMX.h | 27 +++++++++++++++ mplot/VisualResourcesMX.h | 1 - mplot/VisualResourcesNoMX.h | 63 +++++++++++++++++++++++++++++++++++ mplot/gl/ssbo_mx.h | 6 ---- mplot/gl/ssbo_nomx.h | 11 ++++--- 8 files changed, 129 insertions(+), 55 deletions(-) diff --git a/mplot/VisualModelImplNoMX.h b/mplot/VisualModelImplNoMX.h index 30f2cf23..6ef6f8d2 100644 --- a/mplot/VisualModelImplNoMX.h +++ b/mplot/VisualModelImplNoMX.h @@ -66,16 +66,6 @@ namespace mplot model->releaseContext = &mplot::VisualBase::release_context; } - void init_instance_data() - { - if constexpr (mplot::gl::version::has_ssbo (glver) == true) { - if (this->instance_data.ready() == false) { this->instance_data.init(); } - if (this->instparam_data.ready() == false) { this->instparam_data.init(); } - } else { - throw std::runtime_error ("Instanced rendering requires OpenGL 4.3 or higher"); - } - } - void set_instance_data (const sm::vvec>& position) final { sm::vvec> c = { mplot::colour::crimson }; @@ -98,36 +88,35 @@ namespace mplot const sm::vvec>& colour, const sm::vvec& alpha, const sm::vvec& scale) final { - if (position.size() < 1) { throw std::runtime_error ("set_instance_data: pass some instance positions in"); } - if (position.size() > this->max_instanced_items()) { throw std::runtime_error ("set_instance_data: not enough space"); } - - size_t j = 0; - this->instance_data.data.resize (mplot::VisualModelBase::floats_per_instance * position.size()); - for (size_t i = 0; i < position.size(); ++i) { - sm::vec p = position[i]; - this->instance_data.data[j++] = p[0]; - this->instance_data.data[j++] = p[1]; - this->instance_data.data[j++] = p[2]; + if (position.size() < 1) { + throw std::runtime_error ("set_instance_data: pass some instance positions in"); + } + if (position.size() > this->max_instances) { + throw std::runtime_error ("set_instance_data: Haven't reserved enough space for that"); + } + if (!this->insert_instance_data) { + throw std::runtime_error ("set_instance_data: Function insert_instance_data is not bound"); + } + if (!this->insert_instparam_data) { + throw std::runtime_error ("set_instance_data: Function insert_instparam_data is not bound"); } - this->instance_count = position.size(); - if (colour.size() != scale.size() || colour.size() != alpha.size()) { throw std::runtime_error ("set_instance_data: params vvecs should all have same size (colour, rotn, scale)"); } - this->instparam_data.data.resize (mplot::VisualModelBase::floats_per_instparam * colour.size()); - j = 0; + for (size_t i = 0; i < position.size(); ++i) { + // Get access to the SSBO in VisualResources and add the 3 floats in position[i] at + // the location defined by this->instance_start + i + this->insert_instance_data (this->instance_start + i, position[i]); + } + this->instance_count = position.size(); + for (size_t i = 0; i < colour.size(); ++i) { - this->instparam_data.data[j++] = colour[i][0]; - this->instparam_data.data[j++] = colour[i][1]; - this->instparam_data.data[j++] = colour[i][2]; - this->instparam_data.data[j++] = alpha[i]; - this->instparam_data.data[j++] = scale[i]; + this->insert_instparam_data (this->instance_start + i, colour[i], alpha[i], scale[i]); } this->instparam_count = colour.size(); - this->instance_data.copy_to_gpu(); - this->instparam_data.copy_to_gpu(); + this->instanced_needs_update (this->parentVis); } //! Common code to call after the vertices have been set up. GL has to have been initialised. @@ -163,8 +152,13 @@ namespace mplot glBindVertexArray(0); // carefully unbind and rebind mplot::gl::Util::checkError (__FILE__, __LINE__); - if (this->flags.test (vm_bools::instanced) && this->instance_data.ready() == false) { - this->init_instance_data(); + if (this->flags.test (vm_bools::instanced) && this->init_instance_data) { + // Here, we cause the SSBOs to be intialized if they haven't already, and we reserve + // some space in the SSBOs for *this model* + this->instance_start = this->init_instance_data (this->parentVis, this->max_instances); + if (this->instance_start == std::numeric_limits::max()) { + throw std::runtime_error ("Failed to reserve space in SSBO"); + } } /* @@ -467,12 +461,6 @@ namespace mplot while (ti != this->texts.end()) { (*ti)->addViewRotation (r); ti++; } } - //! Shader Storage Buffer Object for instanced rendering - mplot::gl::ssbo::instance_index, - float, mplot::VisualModelBase::max_instance_floats> instance_data; - mplot::gl::ssbo::instparam_index, - float, mplot::VisualModelBase::max_instparam_floats> instparam_data; - protected: //! A vector of pointers to text models that should be rendered. diff --git a/mplot/VisualNoMX.h b/mplot/VisualNoMX.h index d4efc038..27086ef0 100644 --- a/mplot/VisualNoMX.h +++ b/mplot/VisualNoMX.h @@ -164,15 +164,18 @@ namespace mplot void bindmodel (std::unique_ptr& model) { mplot::VisualBase::template bindmodel (model); - model->setContext = &mplot::VisualBase::set_context; - model->releaseContext = &mplot::VisualBase::release_context; + this->bindextra (model); } + // GL dependent function binds template void bindextra (std::unique_ptr& model) { model->setContext = &mplot::VisualBase::set_context; model->releaseContext = &mplot::VisualBase::release_context; + model->init_instance_data = &mplot::VisualOwnableNoMX::init_instance_data; + model->insert_instance_data = &mplot::VisualOwnableNoMX::insert_instance_data; + model->insert_instparam_data = &mplot::VisualOwnableNoMX::insert_instparam_data; } /* diff --git a/mplot/VisualOwnableMX.h b/mplot/VisualOwnableMX.h index cb335fe7..6b67f6c8 100644 --- a/mplot/VisualOwnableMX.h +++ b/mplot/VisualOwnableMX.h @@ -469,7 +469,6 @@ namespace mplot // Use coordArrowsOffset to set the location of the CoordArrows *scene* this->coordArrows = std::make_unique>(); - this->coordArrows->name = "Scene axes"; // For CoordArrows, because we don't add via Visual::addVisualModel(), we // have to set the get_shaderprogs function here: this->bindmodel (this->coordArrows); @@ -480,7 +479,6 @@ namespace mplot // Create 'user frame of reference object' this->userFrame = std::make_unique>(); - this->userFrame->name = "User frame of ref object"; this->bindmodel (this->userFrame); this->userFrame->init (sm::vec{}, sm::vec{0.0f, 0.0f, -100.0f}, sm::vec{0.1f, 0.1f, 1.0f}, 0.05f, @@ -495,7 +493,6 @@ namespace mplot // Set up the title, which may or may not be rendered mplot::TextFeatures title_tf(0.035f, 64); this->textModel = std::make_unique> (title_tf); - this->textModel->name = "Title text"; this->bindmodel (this->textModel); this->textModel->setSceneTranslation ({0.0f, 0.0f, 0.0f}); this->textModel->setupText (this->title); diff --git a/mplot/VisualOwnableNoMX.h b/mplot/VisualOwnableNoMX.h index b1690c3f..af8f7d41 100644 --- a/mplot/VisualOwnableNoMX.h +++ b/mplot/VisualOwnableNoMX.h @@ -253,6 +253,11 @@ namespace mplot this->userFrame->render(); } + if (this->haveInstanced() && this->instancedNeedsUpdate()) { + this->copy_instance_data_to_gpu(); + this->instancedNeedsUpdate (false); + } + auto vmi = this->vm.begin(); while (vmi != this->vm.end()) { if ((*vmi)->twodimensional() == true) { @@ -345,6 +350,28 @@ namespace mplot return tm->getTextGeometry(); } + static unsigned int init_instance_data ([[maybe_unused]]mplot::VisualBase* _v, const unsigned int n_to_reserve) + { + unsigned int reservation = mplot::VisualResourcesNoMX::i().init_instance_ssbo (n_to_reserve); + return reservation; + } + + static void insert_instance_data (const unsigned int instance_idx, const sm::vec& coord) + { + mplot::VisualResourcesNoMX::i().insert_instance_data (instance_idx, coord); + } + + static void insert_instparam_data (const unsigned int instance_idx, + const std::array& colour, const float& alpha, const float& scale) + { + mplot::VisualResourcesNoMX::i().insert_instparam_data (instance_idx, colour, alpha, scale); + } + + static void copy_instance_data_to_gpu() + { + mplot::VisualResourcesNoMX::i().copy_instance_ssbo_to_gpu(); + } + protected: // Initialize OpenGL shaders, set some flags (Alpha, Anti-aliasing), read in any external // state from json, and set up the coordinate arrows and any VisualTextModels that will be diff --git a/mplot/VisualResourcesMX.h b/mplot/VisualResourcesMX.h index 8fd67d34..b275b5db 100644 --- a/mplot/VisualResourcesMX.h +++ b/mplot/VisualResourcesMX.h @@ -170,7 +170,6 @@ namespace mplot //! Shader Storage Buffer Object for instanced rendering - this holds colour, alpha and scale mplot::gl::ssbo::instparam_index, float, mplot::VisualResourcesBase::max_instparam_floats> instparam_data; - }; } // namespace mplot diff --git a/mplot/VisualResourcesNoMX.h b/mplot/VisualResourcesNoMX.h index ee342f8a..4334ad8a 100644 --- a/mplot/VisualResourcesNoMX.h +++ b/mplot/VisualResourcesNoMX.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace mplot { @@ -109,6 +110,68 @@ namespace mplot } else { f++; } } } + + /*! + * We also manage some programm-wide SSBO objects for instanced rendering + * VisualResourcesdata in . Reserve n_to_reserve instances of data in the SSBOs. Return the + * start offset into the buffers in terms of number of instances + */ + unsigned int init_instance_ssbo (const unsigned int n_to_reserve) + { + unsigned int reservation = std::numeric_limits::max(); + if constexpr (mplot::gl::version::has_ssbo (glver) == true) { + if (this->instance_data.ready() == false) { this->instance_data.init(); } + if (this->instparam_data.ready() == false) { this->instparam_data.init(); } + if (n_to_reserve + this->instance_top <= this->max_instances) { + reservation = this->instance_top; + this->instance_top += n_to_reserve; + this->instance_data.resize (this->instance_top * this->floats_per_instance); + this->instparam_data.resize (this->instance_top * this->floats_per_instparam); + } + } else { + throw std::runtime_error ("Instanced rendering requires OpenGL 4.3 or higher"); + } + return reservation; + } + + void insert_instance_data (const unsigned int instance_idx, const sm::vec& coord) + { + if (instance_idx >= this->max_instances) { + throw std::runtime_error ("insert_instance_data: bad instance_idx"); + } + unsigned int cur_fidx = instance_idx * this->floats_per_instance; + this->instance_data.data[cur_fidx++] = coord[0]; + this->instance_data.data[cur_fidx++] = coord[1]; + this->instance_data.data[cur_fidx++] = coord[2]; + } + + void insert_instparam_data (const unsigned int instance_idx, + const std::array& colour, const float& alpha, const float& scale) + { + if (instance_idx >= this->max_instances) { + throw std::runtime_error ("insert_instparam_data: bad instance_idx"); + } + unsigned int cur_fidx = instance_idx * this->floats_per_instparam; + this->instparam_data.data[cur_fidx++] = colour[0]; + this->instparam_data.data[cur_fidx++] = colour[1]; + this->instparam_data.data[cur_fidx++] = colour[2]; + this->instparam_data.data[cur_fidx++] = alpha; + this->instparam_data.data[cur_fidx++] = scale; + } + + void copy_instance_ssbo_to_gpu() + { + if (this->instance_data.ready()) { this->instance_data.copy_to_gpu(); } + if (this->instparam_data.ready()) { this->instparam_data.copy_to_gpu(); } + } + + //! Shader Storage Buffer Object for instanced rendering - this holds positions only + mplot::gl::ssbo::instance_index, + float, mplot::VisualResourcesBase::max_instance_floats> instance_data; + //! Shader Storage Buffer Object for instanced rendering - this holds colour, alpha and scale + mplot::gl::ssbo::instparam_index, + float, mplot::VisualResourcesBase::max_instparam_floats> instparam_data; + }; } // namespace mplot diff --git a/mplot/gl/ssbo_mx.h b/mplot/gl/ssbo_mx.h index 7544723a..814a5caa 100644 --- a/mplot/gl/ssbo_mx.h +++ b/mplot/gl/ssbo_mx.h @@ -93,15 +93,11 @@ namespace mplot::gl if (_data.size() > N) { throw std::runtime_error ("Too big"); } // Update local cached version of data, a portion of which we're about to write to the SSBO - //std::cout << "Copy " << _data.size() << " elements from _data[0] to this->data[" << offset << "]\n"; this->data.resize (_data.size()); for (unsigned int i = 0; i < _data.size(); ++i) { this->data[i + offset] = _data[i]; } - //std::cout << "this->data is now\n\n" << this->data << std::endl; - this->glfn->BindBufferBase (GL_SHADER_STORAGE_BUFFER, index, this->name); mplot::gl::Util::checkError (__FILE__, __LINE__, this->glfn); - std::cout << "Mapping buffer range at offset " << offset << " floats = " << (offset * sizeof(T)) << " bytes" << std::endl; T* cpuptr = static_cast(this->glfn->MapBufferRange (GL_SHADER_STORAGE_BUFFER, offset * sizeof(T), _data.size() * sizeof(T), GL_MAP_WRITE_BIT)); @@ -111,9 +107,7 @@ namespace mplot::gl return; } // Note: cpuptr is a 'pointer to the beginning of the mapped range' and so we don't incorporate offset again, below: - std::cout << "About to copy " << _data.size() << " things (prolly floats)...\n"; for (unsigned int i = 0; i < _data.size(); ++i) { - std::cout << "Writing value " << " _data[i] = " << _data[i] << " into mapped range" << std::endl; cpuptr[i] = _data[i]; } diff --git a/mplot/gl/ssbo_nomx.h b/mplot/gl/ssbo_nomx.h index 96b4a296..24c4c222 100644 --- a/mplot/gl/ssbo_nomx.h +++ b/mplot/gl/ssbo_nomx.h @@ -21,7 +21,7 @@ namespace mplot::gl // @tparam index: The index of the buffer, used in the GLSL // @tparam T: The type of the data in the SSBO // @tparam N: The max number of elements of type T in the SSBO - template // Could add version template params if necessary, to select correct gl function calls + template struct ssbo { // The name of the buffer, generated with glGenBuffers() @@ -31,6 +31,12 @@ namespace mplot::gl bool ready() const { return this->name != 0u; } + void resize (std::size_t sz) + { + if (sz > N) { throw std::runtime_error ("ssbo: can't resize to this size"); } + this->data.resize (sz); + } + ssbo() {} ~ssbo() {} @@ -88,7 +94,6 @@ namespace mplot::gl glBindBufferBase (GL_SHADER_STORAGE_BUFFER, index, this->name); mplot::gl::Util::checkError (__FILE__, __LINE__); - std::cout << "Mapping buffer range at offset " << offset << " floats = " << (offset * sizeof(T)) << " bytes" << std::endl; T* cpuptr = static_cast(glMapBufferRange (GL_SHADER_STORAGE_BUFFER, offset * sizeof(T), _data.size() * sizeof(T), GL_MAP_WRITE_BIT)); @@ -98,9 +103,7 @@ namespace mplot::gl return; } // Note: cpuptr is a 'pointer to the beginning of the mapped range' and so we don't incorporate offset again, below: - std::cout << "About to copy " << _data.size() << " things (prolly floats)...\n"; for (unsigned int i = 0; i < _data.size(); ++i) { - std::cout << "Writing value " << " _data[i] = " << _data[i] << " into mapped range" << std::endl; cpuptr[i] = _data[i]; }