1 #include <msp/core/algorithm.h>
2 #include <msp/core/maputils.h>
4 #include "instancearray.h"
7 #include "objectinstance.h"
10 #include "technique.h"
17 InstanceArrayBase::InstanceArrayBase(const Object &o, size_t s):
20 default_count(max<size_t>(4096U/instance_size, 8U))
22 const Technique *tech = object.get_technique();
23 for(const auto &kvp: tech->get_methods())
25 const Program *shprog = kvp.second.get_shader_program();
27 throw invalid_argument("InstanceArray::InstanceArray");
29 int loc = shprog->get_attribute_location("instance_transform");
31 matrix_location = loc;
32 else if(loc!=matrix_location)
33 throw invalid_argument("InstanceArray::InstanceArray");
36 instance_data.set_format((RAW_ATTRIB4,matrix_location, RAW_ATTRIB4,matrix_location+1, RAW_ATTRIB4,matrix_location+2));
37 const VertexFormat &fmt = instance_data.get_format();
38 matrix_offset = fmt.offset((RAW_ATTRIB4,matrix_location));
40 instance_buffer = new Buffer;
41 instance_data.use_buffer(instance_buffer);
43 const Mesh *mesh = object.get_mesh();
45 vtx_setup.set_format_instanced(mesh->get_vertices().get_format(), fmt);
46 vtx_setup.set_vertex_array(mesh->get_vertices());
47 vtx_setup.set_index_buffer(*mesh->get_index_buffer(), mesh->get_batches().front().get_index_type());
48 vtx_setup.set_instance_array(instance_data);
51 InstanceArrayBase::~InstanceArrayBase()
53 delete instance_buffer;
54 for(Block &b: storage)
58 void InstanceArrayBase::add_block(size_t count)
60 storage.emplace_back();
61 Block &block = storage.back();
62 block.begin = new char[count*instance_size];
63 block.end = block.begin+count*instance_size;
65 size_t block_index = storage.size()-1;
66 size_t base = slots.size();
67 slots.resize(base+count);
68 for(size_t i=0; i<count; ++i)
70 Slot &slot = slots[base+i];
71 slot.next_free = base+i+1;
72 slot.block_index = block_index;
73 slot.index_in_block = i;
77 slots[last_free].next_free = base;
81 last_free = slots.size()-1;
84 slots.back().next_free = first_free;
87 size_t InstanceArrayBase::allocate()
90 add_block(default_count);
92 size_t index = first_free;
93 Slot &slot = slots[index];
94 if(first_free==last_free)
101 first_free = slot.next_free;
102 slots[last_free].next_free = first_free;
106 slot.array_index = instance_count++;
107 if(instance_data.size()<instance_count)
109 instance_data.append();
110 array_order.push_back(index);
113 array_order[slot.array_index] = index;
118 char *InstanceArrayBase::get_address(size_t index) const
120 const Slot &s = slots[index];
121 return storage[s.block_index].begin+s.index_in_block*instance_size;
124 size_t InstanceArrayBase::find_index(char *addr) const
127 for(const Block &b: storage)
129 if(addr>=b.begin && addr<b.end)
130 return base+(addr-b.begin)/instance_size;
131 base += (b.end-b.begin)/instance_size;
134 throw out_of_range("InstanceArrayBase::find_index");
137 void InstanceArrayBase::release(size_t index)
139 Slot &slot = slots[index];
142 if(slot.array_index<instance_count)
144 size_t swap_index = array_order[instance_count];
145 unsigned stride = instance_data.get_format().stride();
146 const char *src = instance_data[swap_index];
147 char *dst = instance_data.modify(slot.array_index);
148 copy(src, src+stride, dst);
149 slots[swap_index].array_index = slot.array_index;
150 array_order[slot.array_index] = swap_index;
151 array_order[instance_count] = -1;
157 slot.next_free = first_free;
158 slots[last_free].next_free = index;
162 slot.next_free = index;
168 void InstanceArrayBase::update_instance_matrix(size_t index, const Matrix &matrix)
170 float *d = reinterpret_cast<float *>(instance_data.modify(slots[index].array_index)+matrix_offset);
171 for(unsigned i=0; i<12; ++i)
172 d[i] = matrix(i/4, i%4);
175 void InstanceArrayBase::render(Renderer &renderer, Tag tag) const
180 const Technique *tech = object.get_technique();
182 throw logic_error("no technique");
183 const RenderMethod *method = tech->find_method(tag);
187 const Mesh *mesh = object.get_mesh();
188 if(instance_buffer->get_size()==0)
189 instance_buffer->storage(instance_data.get_required_buffer_size(), STREAMING);
191 Renderer::Push push(renderer);
192 renderer.set_pipeline_key(this, tag.id);
193 method->apply(renderer);
194 mesh->draw_instanced(renderer, vtx_setup, instance_count);