#include <msp/core/algorithm.h>
#include <msp/core/maputils.h>
-#include <msp/gl/extensions/arb_draw_instanced.h>
-#include <msp/gl/extensions/arb_instanced_arrays.h>
-#include <msp/gl/extensions/arb_vertex_array_object.h>
-#include <msp/gl/extensions/arb_vertex_shader.h>
#include "buffer.h"
-#include "camera.h"
#include "instancearray.h"
#include "mesh.h"
#include "object.h"
#include "objectinstance.h"
+#include "program.h"
#include "renderer.h"
#include "technique.h"
-#include "vertexsetup.h"
using namespace std;
namespace Msp {
namespace GL {
-InstanceArray::InstanceArray(const Object &o):
+InstanceArrayBase::InstanceArrayBase(const Object &o, size_t s):
object(o),
- instance_data(0),
- instance_buffer(0),
- vtx_setup(0),
- matrix_location(-1),
- matrix_offset(0)
+ instance_size(s),
+ default_count(max<size_t>(4096U/instance_size, 8U))
{
const Technique *tech = object.get_technique();
- const Technique::PassMap &passes = tech->get_passes();
- for(Technique::PassMap::const_iterator i=passes.begin(); i!=passes.end(); ++i)
+ for(const auto &kvp: tech->get_methods())
{
- const Program *shprog = i->second.get_shader_program();
+ const Program *shprog = kvp.second.get_shader_program();
if(!shprog)
throw invalid_argument("InstanceArray::InstanceArray");
throw invalid_argument("InstanceArray::InstanceArray");
}
- if(ARB_vertex_array_object && ARB_instanced_arrays && ARB_draw_instanced)
- {
- instance_data = new VertexArray((RAW_ATTRIB4,matrix_location, RAW_ATTRIB4,matrix_location+1, RAW_ATTRIB4,matrix_location+2));
- const VertexFormat &fmt = instance_data->get_format();
- matrix_offset = fmt.offset(make_indexed_attribute(RAW_ATTRIB4, matrix_location));
+ instance_data.set_format((RAW_ATTRIB4,matrix_location, RAW_ATTRIB4,matrix_location+1, RAW_ATTRIB4,matrix_location+2));
+ const VertexFormat &fmt = instance_data.get_format();
+ matrix_offset = fmt.offset((RAW_ATTRIB4,matrix_location));
- instance_buffer = new Buffer(ARRAY_BUFFER);
- instance_data->use_buffer(instance_buffer);
+ instance_buffer = new Buffer;
+ instance_data.use_buffer(instance_buffer);
- vtx_setup = new VertexSetup;
- vtx_setup->set_vertex_array(object.get_mesh()->get_vertices());
- vtx_setup->set_index_buffer(*object.get_mesh()->get_index_buffer());
- vtx_setup->set_instance_array(instance_data);
- }
- else
- static Require req(ARB_vertex_shader);
+ const Mesh *mesh = object.get_mesh();
+
+ vtx_setup.set_format_instanced(mesh->get_vertices().get_format(), fmt);
+ vtx_setup.set_vertex_array(mesh->get_vertices());
+ vtx_setup.set_index_buffer(*mesh->get_index_buffer(), mesh->get_batches().front().get_index_type());
+ vtx_setup.set_instance_array(instance_data);
}
-InstanceArray::~InstanceArray()
+InstanceArrayBase::~InstanceArrayBase()
{
- for(vector<ObjectInstance *>::iterator i=instances.begin(); i!=instances.end(); ++i)
- delete *i;
- delete vtx_setup;
- delete instance_data;
delete instance_buffer;
+ for(Block &b: storage)
+ delete[] b.begin;
}
-void InstanceArray::append(ObjectInstance *inst)
+void InstanceArrayBase::add_block(size_t count)
{
- instances.push_back(inst);
- if(instance_data)
+ storage.emplace_back();
+ Block &block = storage.back();
+ block.begin = new char[count*instance_size];
+ block.end = block.begin+count*instance_size;
+
+ size_t block_index = storage.size()-1;
+ size_t base = slots.size();
+ slots.resize(base+count);
+ for(size_t i=0; i<count; ++i)
{
- if(instance_data->size()<instances.size())
- {
- instance_data->append();
- unsigned req_size = instance_data->get_required_buffer_size();
- if(instance_buffer->get_size()>0 && instance_buffer->get_size()<req_size)
- {
- delete instance_buffer;
- instance_buffer = new Buffer(ARRAY_BUFFER);
- instance_data->use_buffer(instance_buffer);
- }
- }
- update_instance_matrix(instances.size()-1);
+ Slot &slot = slots[base+i];
+ slot.next_free = base+i+1;
+ slot.block_index = block_index;
+ slot.index_in_block = i;
}
+
+ if(first_free>0)
+ slots[last_free].next_free = base;
+ else
+ {
+ first_free = base;
+ last_free = slots.size()-1;
+ }
+
+ slots.back().next_free = first_free;
}
-void InstanceArray::remove(ObjectInstance &inst)
+size_t InstanceArrayBase::allocate()
{
- vector<ObjectInstance *>::iterator i = find(instances, &inst);
- if(i==instances.end())
- throw key_error(&inst);
+ if(first_free<0)
+ add_block(default_count);
- delete *i;
- *i = instances.back();
- instances.pop_back();
+ size_t index = first_free;
+ Slot &slot = slots[index];
+ if(first_free==last_free)
+ {
+ first_free = -1;
+ last_free = -1;
+ }
+ else
+ {
+ first_free = slot.next_free;
+ slots[last_free].next_free = first_free;
+ }
+
+ slot.used = true;
+ slot.array_index = instance_count++;
+ if(instance_data.size()<instance_count)
+ {
+ instance_data.append();
+ array_order.push_back(index);
+ }
+ else
+ array_order[slot.array_index] = index;
+
+ return index;
}
-void InstanceArray::update_instance_matrix(unsigned index)
+char *InstanceArrayBase::get_address(size_t index) const
{
- if(!instance_data)
- return;
+ const Slot &s = slots[index];
+ return storage[s.block_index].begin+s.index_in_block*instance_size;
+}
- const Matrix &m = *instances[index]->get_matrix();
+size_t InstanceArrayBase::find_index(char *addr) const
+{
+ size_t base = 0;
+ for(const Block &b: storage)
+ {
+ if(addr>=b.begin && addr<b.end)
+ return base+(addr-b.begin)/instance_size;
+ base += (b.end-b.begin)/instance_size;
+ }
- float *d = instance_data->modify(instances.size()-1);
- for(unsigned i=0; i<12; ++i)
- d[matrix_offset+i] = m(i/4, i%4);
+ throw out_of_range("InstanceArrayBase::find_index");
}
-void InstanceArray::render(Renderer &renderer, Tag tag) const
+void InstanceArrayBase::release(size_t index)
{
- if(instances.empty())
- return;
+ Slot &slot = slots[index];
+
+ --instance_count;
+ if(slot.array_index<instance_count)
+ {
+ size_t swap_index = array_order[instance_count];
+ unsigned stride = instance_data.get_format().stride();
+ const char *src = instance_data[swap_index];
+ char *dst = instance_data.modify(slot.array_index);
+ copy(src, src+stride, dst);
+ slots[swap_index].array_index = slot.array_index;
+ array_order[slot.array_index] = swap_index;
+ array_order[instance_count] = -1;
+ }
- if(instance_data)
+ slot.used = false;
+ if(first_free>=0)
{
- const Technique *tech = object.get_technique();
- if(!tech)
- throw logic_error("no technique");
- const RenderPass *pass = tech->find_pass(tag);
- if(!pass)
- return;
-
- const Mesh *mesh = object.get_mesh();
- mesh->get_vertices().refresh();
- if(instance_buffer->get_size()==0)
- instance_buffer->storage(instance_data->get_required_buffer_size());
- instance_data->refresh();
-
- Renderer::Push push(renderer);
- pass->apply(renderer);
- mesh->draw_instanced(renderer, *vtx_setup, instances.size());
+ slot.next_free = first_free;
+ slots[last_free].next_free = index;
}
else
{
- for(vector<ObjectInstance *>::const_iterator i=instances.begin(); i!=instances.end(); ++i)
- {
- const Matrix &m = *(*i)->get_matrix();
- for(unsigned j=0; j<3; ++j)
- glVertexAttrib4f(matrix_location+j, m(j, 0), m(j, 1), m(j, 2), m(j, 3));
- (*i)->render(renderer, tag);
- }
+ slot.next_free = index;
+ first_free = index;
}
+ last_free = index;
+}
+
+void InstanceArrayBase::update_instance_matrix(size_t index, const Matrix &matrix)
+{
+ float *d = reinterpret_cast<float *>(instance_data.modify(slots[index].array_index)+matrix_offset);
+ for(unsigned i=0; i<12; ++i)
+ d[i] = matrix(i/4, i%4);
+}
+
+void InstanceArrayBase::render(Renderer &renderer, Tag tag) const
+{
+ if(!instance_count)
+ return;
+
+ const Technique *tech = object.get_technique();
+ if(!tech)
+ throw logic_error("no technique");
+ const RenderMethod *method = tech->find_method(tag);
+ if(!method)
+ return;
+
+ const Mesh *mesh = object.get_mesh();
+ if(instance_buffer->get_size()==0)
+ instance_buffer->storage(instance_data.get_required_buffer_size(), STREAMING);
+
+ Renderer::Push push(renderer);
+ renderer.set_pipeline_key(this, tag.id);
+ method->apply(renderer);
+ mesh->draw_instanced(renderer, vtx_setup, instance_count);
+}
+
+
+DataFile::Loader::ActionMap InstanceArrayBase::Loader::shared_actions;
+
+InstanceArrayBase::Loader::Loader(InstanceArrayBase &o):
+ ObjectLoader<InstanceArrayBase>(o)
+{
+ set_actions(shared_actions);
+}
+
+void InstanceArrayBase::Loader::init_actions()
+{
+ add("instance", &Loader::instance);
}
} // namespace GL