]> git.tdb.fi Git - libs/gl.git/blob - source/batch.cpp
Implement automatic unloading when a total size limit is exceeded
[libs/gl.git] / source / batch.cpp
1 #include <msp/gl/extensions/ext_draw_range_elements.h>
2 #include <msp/gl/extensions/nv_primitive_restart.h>
3 #include "batch.h"
4 #include "bindable.h"
5 #include "buffer.h"
6 #include "error.h"
7 #include "mesh.h"
8 #include "vertexarray.h"
9
10 using namespace std;
11
12 namespace {
13
14 template<typename T>
15 void append(vector<unsigned char> &data, T i)
16 {
17         data.insert(data.end(), sizeof(T), 0);
18         *(T *)(&data[data.size()-sizeof(T)]) = i;
19 }
20
21 template<typename T, typename U>
22 U convert(T n)
23 {
24         if(!static_cast<T>(~n))
25                 return ~0;
26         else
27                 return n;
28 }
29
30 template<typename T, typename U>
31 void expand(vector<unsigned char> &data)
32 {
33         unsigned count = data.size()/sizeof(T);
34         data.resize(count*sizeof(U));
35         for(unsigned i=count; i--;)
36                 *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
37 }
38
39 template<typename T, typename U>
40 void shrink(vector<unsigned char> &data)
41 {
42         unsigned count = data.size()/sizeof(T);
43         for(unsigned i=0; i<count; ++i)
44                 *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
45         data.resize(count*sizeof(U));
46 }
47
48 }
49
50 namespace Msp {
51 namespace GL {
52
53 unsigned Batch::restart_index = 0;
54
55 Batch::Batch(PrimitiveType t):
56         prim_type(t),
57         data_type(UNSIGNED_BYTE),
58         min_index(0),
59         max_index(0),
60         restart(false)
61 {
62         /* XXX Should probably provide a fallback to glDrawElements since this class
63         is pretty much required to render anything. */
64         static Require _req(EXT_draw_range_elements);
65 }
66
67 Batch::~Batch()
68 {
69 }
70
71 void Batch::set_data_type(DataType t)
72 {
73         if(t!=UNSIGNED_BYTE && t!=UNSIGNED_SHORT && t!=UNSIGNED_INT)
74                 throw invalid_argument("Batch::set_data_type");
75         if(t==UNSIGNED_BYTE && max_index>0xFE)
76                 throw invalid_operation("Batch::set_data_type");
77         else if(t==UNSIGNED_SHORT && max_index>0xFFFE)
78                 throw invalid_operation("Batch::set_data_type");
79
80         if(data_type==UNSIGNED_BYTE && t==UNSIGNED_SHORT)
81                 expand<unsigned char, unsigned short>(data);
82         else if(data_type==UNSIGNED_BYTE && t==UNSIGNED_INT)
83                 expand<unsigned char, unsigned>(data);
84         else if(data_type==UNSIGNED_SHORT && t==UNSIGNED_INT)
85                 expand<unsigned short, unsigned>(data);
86         else if(data_type==UNSIGNED_INT && t==UNSIGNED_BYTE)
87                 shrink<unsigned, unsigned char>(data);
88         else if(data_type==UNSIGNED_INT && t==UNSIGNED_SHORT)
89                 shrink<unsigned, unsigned short>(data);
90         else if(data_type==UNSIGNED_SHORT && t==UNSIGNED_BYTE)
91                 shrink<unsigned short, unsigned char>(data);
92
93         data_type = t;
94         update_offset();
95         dirty = true;
96 }
97
98 Batch &Batch::append(unsigned i)
99 {
100         append_index(i);
101
102         update_offset();
103         dirty = true;
104
105         return *this;
106 }
107
108 void Batch::append(const vector<unsigned> &ind)
109 {
110         if(ind.empty())
111                 return;
112
113         data.reserve(data.size()+ind.size()*get_index_size());
114         for(vector<unsigned>::const_iterator i=ind.begin(); i!=ind.end(); ++i)
115                 append_index(*i);
116
117         update_offset();
118         dirty = true;
119 }
120
121 bool Batch::can_append(PrimitiveType other_type)
122 {
123         if(other_type!=prim_type)
124                 return false;
125         else if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN || prim_type==POLYGON)
126                 return NV_primitive_restart;
127         else
128                 return true;
129 }
130
131 void Batch::append(const Batch &other)
132 {
133         if(other.prim_type!=prim_type)
134                 throw invalid_argument("Batch::append");
135         if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN || prim_type==POLYGON)
136                 static Require _req(NV_primitive_restart);
137
138         if(other.data.empty())
139                 return;
140
141         if(prim_type==POINTS || prim_type==LINES || prim_type==TRIANGLES || prim_type==QUADS)
142                 ;
143         else if(NV_primitive_restart)
144         {
145                 restart = true;
146                 if(data_type==UNSIGNED_SHORT)
147                         ::append<unsigned short>(data, 0xFFFF);
148                 else if(data_type==UNSIGNED_INT)
149                         ::append<unsigned>(data, 0xFFFFFFFF);
150                 else
151                         data.push_back(0xFF);
152         }
153         else if(prim_type==TRIANGLE_STRIP)
154         {
155                 append(get_index(size()-1));
156                 append(other.get_index(0));
157                 if(size()&1)
158                         append(other.get_index(0));
159         }
160         else if(prim_type==QUAD_STRIP)
161         {
162                 append(get_index(size()-1));
163                 append(get_index(size()-1));
164                 append(other.get_index(0));
165                 append(other.get_index(0));
166         }
167
168         unsigned count = other.size();
169         for(unsigned i=0; i<count; ++i)
170                 append_index(other.get_index(i));
171
172         update_offset();
173         dirty = true;
174 }
175
176 void Batch::append_index(unsigned i)
177 {
178         if(data.empty())
179                 min_index = max_index = i;
180         else
181         {
182                 min_index = min(min_index, i);
183                 max_index = max(max_index, i);
184         }
185
186         if((data_type==UNSIGNED_BYTE || data_type==UNSIGNED_SHORT) && max_index>0xFFFE)
187                 set_data_type(UNSIGNED_INT);
188         else if(data_type==UNSIGNED_BYTE && max_index>0xFE)
189                 set_data_type(UNSIGNED_SHORT);
190
191         if(data_type==UNSIGNED_SHORT)
192                 ::append<unsigned short>(data, i);
193         else if(data_type==UNSIGNED_INT)
194                 ::append<unsigned>(data, i);
195         else
196                 data.push_back(i);
197 }
198
199 void Batch::upload_data() const
200 {
201         get_buffer()->sub_data(get_offset(), data.size(), &data[0]);
202 }
203
204 unsigned Batch::get_index_size() const
205 {
206         if(data_type==UNSIGNED_SHORT)
207                 return sizeof(unsigned short);
208         else if(data_type==UNSIGNED_INT)
209                 return sizeof(unsigned);
210         return sizeof(unsigned char);
211 }
212
213 unsigned Batch::get_index(unsigned i) const
214 {
215         if(data_type==UNSIGNED_SHORT)
216                 return *(unsigned short *)&data[i*sizeof(unsigned short)];
217         else if(data_type==UNSIGNED_INT)
218                 return *(unsigned *)&data[i*sizeof(unsigned )];
219         else
220                 return data[i];
221 }
222
223 void Batch::draw() const
224 {
225         if(restart)
226         {
227                 unsigned index;
228                 if(data_type==UNSIGNED_SHORT)
229                         index = 0xFFFF;
230                 else if(data_type==UNSIGNED_INT)
231                         index = 0xFFFFFFFF;
232                 else
233                         index = 0xFF;
234
235                 if(index!=restart_index)
236                 {
237                         if(!restart_index)
238                                 glEnableClientState(GL_PRIMITIVE_RESTART_NV);
239                         glPrimitiveRestartIndexNV(index);
240                         restart_index = index;
241                 }
242         }
243         else if(restart_index && restart_index<=max_index)
244         {
245                 glDisableClientState(GL_PRIMITIVE_RESTART_NV);
246                 restart_index = 0;
247         }
248
249         Buffer *ibuf = get_buffer();
250         BindRestore _bind_ibuf(ibuf, ELEMENT_ARRAY_BUFFER);
251         if(ibuf)
252         {
253                 if(dirty)
254                         update_buffer();
255
256                 glDrawRangeElements(prim_type, min_index, max_index, size(), data_type, reinterpret_cast<void *>(get_offset()));
257         }
258         else
259                 glDrawRangeElements(prim_type, min_index, max_index, size(), data_type, &data[0]);
260 }
261
262
263 Batch::Loader::Loader(Batch &b):
264         DataFile::ObjectLoader<Batch>(b)
265 {
266         add("indices", &Loader::indices);
267 }
268
269 void Batch::Loader::indices(const vector<unsigned> &ind)
270 {
271         obj.append(ind);
272 }
273
274 } // namespace GL
275 } // namespace Msp