]> git.tdb.fi Git - libs/gl.git/blob - source/render/instancearray.cpp
Redesign InstanceArray
[libs/gl.git] / source / render / instancearray.cpp
1 #include <msp/core/algorithm.h>
2 #include <msp/core/maputils.h>
3 #include "buffer.h"
4 #include "instancearray.h"
5 #include "mesh.h"
6 #include "object.h"
7 #include "objectinstance.h"
8 #include "program.h"
9 #include "renderer.h"
10 #include "technique.h"
11
12 using namespace std;
13
14 namespace Msp {
15 namespace GL {
16
17 InstanceArrayBase::InstanceArrayBase(const Object &o, size_t s):
18         object(o),
19         instance_size(s),
20         default_count(max<size_t>(4096U/instance_size, 8U))
21 {
22         const Technique *tech = object.get_technique();
23         for(const auto &kvp: tech->get_methods())
24         {
25                 const Program *shprog = kvp.second.get_shader_program();
26                 if(!shprog)
27                         throw invalid_argument("InstanceArray::InstanceArray");
28
29                 int loc = shprog->get_attribute_location("instance_transform");
30                 if(matrix_location<0)
31                         matrix_location = loc;
32                 else if(loc!=matrix_location)
33                         throw invalid_argument("InstanceArray::InstanceArray");
34         }
35
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));
39
40         instance_buffer = new Buffer;
41         instance_data.use_buffer(instance_buffer);
42
43         const Mesh *mesh = object.get_mesh();
44
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);
49 }
50
51 InstanceArrayBase::~InstanceArrayBase()
52 {
53         delete instance_buffer;
54         for(Block &b: storage)
55                 delete[] b.begin;
56 }
57
58 void InstanceArrayBase::add_block(size_t count)
59 {
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;
64
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)
69         {
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;
74         }
75
76         if(first_free>0)
77                 slots[last_free].next_free = base;
78         else
79         {
80                 first_free = base;
81                 last_free = slots.size()-1;
82         }
83
84         slots.back().next_free = first_free;
85 }
86
87 size_t InstanceArrayBase::allocate()
88 {
89         if(first_free<0)
90                 add_block(default_count);
91
92         size_t index = first_free;
93         Slot &slot = slots[index];
94         if(first_free==last_free)
95         {
96                 first_free = -1;
97                 last_free = -1;
98         }
99         else
100         {
101                 first_free = slot.next_free;
102                 slots[last_free].next_free = first_free;
103         }
104
105         slot.used = true;
106         slot.array_index = instance_count++;
107         if(instance_data.size()<instance_count)
108         {
109                 instance_data.append();
110                 array_order.push_back(index);
111         }
112         else
113                 array_order[slot.array_index] = index;
114
115         return index;
116 }
117
118 char *InstanceArrayBase::get_address(size_t index) const
119 {
120         const Slot &s = slots[index];
121         return storage[s.block_index].begin+s.index_in_block*instance_size;
122 }
123
124 size_t InstanceArrayBase::find_index(char *addr) const
125 {
126         size_t base = 0;
127         for(const Block &b: storage)
128         {
129                 if(addr>=b.begin && addr<b.end)
130                         return base+(addr-b.begin)/instance_size;
131                 base += (b.end-b.begin)/instance_size;
132         }
133
134         throw out_of_range("InstanceArrayBase::find_index");
135 }
136
137 void InstanceArrayBase::release(size_t index)
138 {
139         Slot &slot = slots[index];
140
141         --instance_count;
142         if(slot.array_index<instance_count)
143         {
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;
152         }
153
154         slot.used = false;
155         if(first_free>=0)
156         {
157                 slot.next_free = first_free;
158                 slots[last_free].next_free = index;
159         }
160         else
161         {
162                 slot.next_free = index;
163                 first_free = index;
164         }
165         last_free = index;
166 }
167
168 void InstanceArrayBase::update_instance_matrix(size_t index, const Matrix &matrix)
169 {
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);
173 }
174
175 void InstanceArrayBase::render(Renderer &renderer, Tag tag) const
176 {
177         if(!instance_count)
178                 return;
179
180         const Technique *tech = object.get_technique();
181         if(!tech)
182                 throw logic_error("no technique");
183         const RenderMethod *method = tech->find_method(tag);
184         if(!method)
185                 return;
186
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);
190
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);
195 }
196
197 } // namespace GL
198 } // namespace Msp