Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions examples/breadcrumbs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ int main()

// A normal, non instanced model. A sphere to orbit around.
auto gv1 = std::make_unique<mplot::GeodesicVisual<float,glver>> (sm::vec<>{}, 0.2f);
gv1->name = "geodesic";
v.bindmodel (gv1);
gv1->iterations = 3;
gv1->cm.setType (mplot::ColourMapType::Tofino);
Expand All @@ -65,21 +66,24 @@ int main()
}

auto isv = std::make_unique<mplot::InstancedScatterVisual<glver>> (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<mplot::InstancedScatterVisual<glver>> (sm::vec<>{0,1,0});
isv = std::make_unique<mplot::InstancedScatterVisual<glver>> (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);

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);

Expand All @@ -101,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
Expand Down
18 changes: 17 additions & 1 deletion mplot/VisualBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ 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,
//! 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
Expand Down Expand Up @@ -238,6 +242,7 @@ namespace mplot
model->get_shaderprogs = &mplot::VisualBase<glver>::get_shaderprogs;
model->get_gprog = &mplot::VisualBase<glver>::get_gprog;
model->get_tprog = &mplot::VisualBase<glver>::get_tprog;
model->instanced_needs_update = &mplot::VisualBase<glver>::instanced_needs_update;
}

/*!
Expand All @@ -248,6 +253,7 @@ namespace mplot
unsigned int addVisualModelId (std::unique_ptr<T>& model)
{
std::unique_ptr<mplot::VisualModel<glver>> 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;
Expand All @@ -260,6 +266,7 @@ namespace mplot
T* addVisualModel (std::unique_ptr<T>& model)
{
std::unique_ptr<mplot::VisualModel<glver>> vmp = std::move(model);
if (vmp->instanced()) { this->state.set (visual_state::haveInstanced, true); }
this->vm.push_back (std::move(vmp));
return static_cast<T*>(this->vm.back().get());
}
Expand Down Expand Up @@ -372,6 +379,8 @@ namespace mplot
static GLuint get_gprog (mplot::VisualBase<glver>* _v) { return _v->shaders.gprog; };
static GLuint get_tprog (mplot::VisualBase<glver>* _v) { return _v->shaders.tprog; };

static void instanced_needs_update (mplot::VisualBase<glver>* _v) { _v->instancedNeedsUpdate (true); }

//! The colour of ambient and diffuse light sources
sm::vec<float, 3> light_colour = { 1.0f, 1.0f, 1.0f };
//! Strength of the ambient light
Expand Down Expand Up @@ -461,6 +470,13 @@ 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); }

//! 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.
Expand Down
8 changes: 5 additions & 3 deletions mplot/VisualDefaultShaders.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
8 changes: 5 additions & 3 deletions mplot/VisualMX.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,17 +165,19 @@ namespace mplot
void bindmodel (std::unique_ptr<T>& model)
{
mplot::VisualBase<glver>::template bindmodel<T> (model); // base class binds
model->setContext = &mplot::VisualBase<glver>::set_context;
model->releaseContext = &mplot::VisualBase<glver>::release_context;
model->get_glfn = &mplot::VisualOwnableMX<glver>::get_glfn;
this->bindextra (model);
}

// The GL-dependent binds
template <typename T>
void bindextra (std::unique_ptr<T>& model)
{
model->setContext = &mplot::VisualBase<glver>::set_context;
model->releaseContext = &mplot::VisualBase<glver>::release_context;
model->get_glfn = &mplot::VisualOwnableMX<glver>::get_glfn;
model->init_instance_data = &mplot::VisualOwnableMX<glver>::init_instance_data;
model->insert_instance_data = &mplot::VisualOwnableMX<glver>::insert_instance_data;
model->insert_instparam_data = &mplot::VisualOwnableMX<glver>::insert_instparam_data;
}

/*
Expand Down
35 changes: 16 additions & 19 deletions mplot/VisualModelBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -718,26 +718,17 @@ 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?
/*!
* 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<glver>::max_instances must be larger than this.
*/
unsigned int max_instances = 16 * 1024;
//! The offset into the SSBOs for the instance data for this VisualModel
unsigned int instance_start = 0;
//! 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)
unsigned int instparam_count = 0;
Expand All @@ -758,6 +749,12 @@ namespace mplot
//! Release OpenGL context. Should call parentVis->releaseContext().
std::function<void(mplot::VisualBase<glver>*)> releaseContext;

//! Init the SSBOs for instanced rendering
std::function<unsigned int(mplot::VisualBase<glver>*, const unsigned int)> init_instance_data;
std::function<void(const unsigned int, const sm::vec<float, 3>&)> insert_instance_data;
std::function<void(const unsigned int, const std::array<float, 3>&, const float, const float)> insert_instparam_data;
std::function<void(mplot::VisualBase<glver>*)> instanced_needs_update;

//! Set up the instance positions (with default params for colour, rotn, scale)
virtual void set_instance_data (const sm::vvec<sm::vec<float, 3>>& position) = 0;

Expand Down
91 changes: 27 additions & 64 deletions mplot/VisualModelImplMX.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include <mplot/VisualModelBase.h>

#include <mplot/gl/util_mx.h>
#include <mplot/gl/ssbo_mx.h>
#include <mplot/VisualTextModel.h>
#include <mplot/TextGeometry.h>

Expand Down Expand Up @@ -71,17 +70,6 @@ namespace mplot
model->releaseContext = &mplot::VisualBase<glver>::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<sm::vec<float, 3>>& position) final
{
sm::vvec<std::array<float, 3>> c = { mplot::colour::crimson };
Expand All @@ -104,60 +92,36 @@ namespace mplot
const sm::vvec<std::array<float, 3>>& colour,
const sm::vvec<float>& alpha, const sm::vvec<float>& 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<glver>::floats_per_instance * position.size());
for (size_t i = 0; i < position.size(); ++i) {
sm::vec<float, 3> 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<glver>::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();
}

#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<sm::vec<float, 3>>& points, const sm::vvec<float>& 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<float> updated(mplot::VisualModelBase<glver>::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["<<i<<"]\n";
sm::vec<float, 3> 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);
this->instanced_needs_update (this->parentVis);
}
#endif

//! Common code to call after the vertices have been set up. GL has to have been initialised.
void postVertexInit() final
Expand Down Expand Up @@ -194,8 +158,13 @@ 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) {
// 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<unsigned int>::max()) {
throw std::runtime_error ("Failed to reserve space in SSBO");
}
}

/*
Expand Down Expand Up @@ -504,12 +473,6 @@ namespace mplot
//! Get the GladGLContext function pointer
std::function<GladGLContext*(mplot::VisualBase<glver>*)> get_glfn;

//! Shader Storage Buffer Object for instanced rendering
mplot::gl::ssbo<mplot::VisualModelBase<glver>::instance_index,
float, mplot::VisualModelBase<glver>::max_instance_floats> instance_data;
mplot::gl::ssbo<mplot::VisualModelBase<glver>::instparam_index,
float, mplot::VisualModelBase<glver>::max_instparam_floats> instparam_data;

protected:

//! A vector of pointers to text models that should be rendered.
Expand Down
Loading