]> git.tdb.fi Git - libs/gl.git/blob - source/render/programdata.cpp
Add a usage parameter to Buffer
[libs/gl.git] / source / render / programdata.cpp
1 #include <msp/core/algorithm.h>
2 #include <msp/debug/demangle.h>
3 #include <msp/io/print.h>
4 #include "buffer.h"
5 #include "color.h"
6 #include "error.h"
7 #include "matrix.h"
8 #include "pipelinestate.h"
9 #include "program.h"
10 #include "programdata.h"
11 #include "uniformblock.h"
12 #include "vector.h"
13
14 using namespace std;
15
16 namespace Msp {
17 namespace GL {
18
19 ProgramData::ProgramData(const Program *p):
20         tied_program(p)
21 { }
22
23 ProgramData::ProgramData(ProgramData &&other):
24         tied_program(other.tied_program),
25         uniforms(move(other.uniforms)),
26         uniform_data(move(other.uniform_data)),
27         generation(other.generation),
28         blocks(move(other.blocks)),
29         programs(move(other.programs)),
30         last_buffer_block(other.last_buffer_block),
31         buffer(other.buffer),
32         dirty(other.dirty),
33         debug_name(move(other.debug_name))
34 {
35         other.blocks.clear();
36         other.buffer = 0;
37 }
38
39 ProgramData::~ProgramData()
40 {
41         for(SharedBlock &b: blocks)
42         {
43                 if(b.indices.type_flag==0xFE)
44                         delete[] b.indices.dynamic.values;
45                 delete b.block;
46         }
47         delete buffer;
48 }
49
50 void ProgramData::uniform(Tag tag, DataType type, unsigned array_size, const void *value)
51 {
52         if(!validate_tag(tag))
53                 return;
54
55         auto i = lower_bound_member(uniforms, tag, &TaggedUniform::tag);
56         if(i==uniforms.end() || i->tag!=tag)
57         {
58                 if(uniforms.size()>=MASK_BITS)
59                         throw too_many_uniforms(tag.str());
60
61                 TaggedUniform tu;
62                 tu.tag = tag;
63                 tu.type = type;
64                 tu.array_size = array_size;
65                 tu.data_offset = uniform_data.size();
66                 tu.data_size = array_size*get_type_size(type);
67                 i = uniforms.insert(i, tu);
68                 uniform_data.resize(tu.data_offset+tu.data_size);
69
70                 mark_dirty(ALL_ONES);
71         }
72         else if(type!=i->type)
73                 throw invalid_operation("ProgramData::uniform");
74         else if(array_size>i->array_size)
75         {
76                 unsigned add_bytes = (array_size-i->array_size)*get_type_size(type);
77                 uniform_data.insert(uniform_data.begin()+i->data_offset+i->data_size, add_bytes, 0);
78                 for(TaggedUniform &u: uniforms)
79                         if(u.data_offset>i->data_offset)
80                                 u.data_offset += add_bytes;
81                 i->array_size = array_size;
82                 i->data_size = array_size*get_type_size(type);
83         }
84
85         const char *val_begin = static_cast<const char *>(value);
86         const char *val_end = val_begin+array_size*get_type_size(type);
87         char *store_begin = uniform_data.data()+i->data_offset;
88         copy(val_begin, val_end, store_begin);
89
90         mark_dirty(1<<(i-uniforms.begin()));
91 }
92
93 bool ProgramData::validate_tag(Tag tag) const
94 {
95 #ifdef DEBUG
96         try
97 #endif
98         {
99                 if(tied_program)
100                 {
101                         const ReflectData::UniformInfo &info = tied_program->get_uniform_info(tag);
102                         if(is_image(info.type))
103                                 throw invalid_operation("ProgramData::uniform");
104                 }
105                 return true;
106         }
107 #ifdef DEBUG
108         catch(const exception &e)
109         {
110                 IO::print(IO::cerr, "Error while setting uniform %s: %s: %s\n", tag, Debug::demangle(typeid(e).name()), e.what());
111                 return false;
112         }
113 #endif
114 }
115
116 void ProgramData::mark_dirty(Mask bits)
117 {
118         if(!dirty)
119                 ++generation;
120         dirty |= bits;
121 }
122
123 void ProgramData::uniform(Tag tag, int v)
124 {
125         uniform(tag, INT, 1, &v);
126 }
127
128 void ProgramData::uniform(Tag tag, unsigned v)
129 {
130         uniform(tag, UNSIGNED_INT, 1, &v);
131 }
132
133 void ProgramData::uniform(Tag tag, float v)
134 {
135         uniform(tag, FLOAT, 1, &v);
136 }
137
138 void ProgramData::uniform(Tag tag, int v0, int v1)
139 {
140         int va[2] = { v0, v1 };
141         uniform2(tag, va);
142 }
143
144 void ProgramData::uniform(Tag tag, unsigned v0, unsigned v1)
145 {
146         unsigned va[2] = { v0, v1 };
147         uniform2(tag, va);
148 }
149
150 void ProgramData::uniform(Tag tag, float v0, float v1)
151 {
152         float va[2] = { v0, v1 };
153         uniform2(tag, va);
154 }
155
156 void ProgramData::uniform2(Tag tag, const int *v)
157 {
158         uniform(tag, INT_VEC2, 1, v);
159 }
160
161 void ProgramData::uniform2(Tag tag, const unsigned *v)
162 {
163         uniform(tag, UINT_VEC2, 1, v);
164 }
165
166 void ProgramData::uniform2(Tag tag, const float *v)
167 {
168         uniform(tag, FLOAT_VEC2, 1, v);
169 }
170
171 void ProgramData::uniform(Tag tag, int v0, int v1, int v2)
172 {
173         int va[3] = { v0, v1, v2 };
174         uniform3(tag, va);
175 }
176
177 void ProgramData::uniform(Tag tag, unsigned v0, unsigned v1, unsigned v2)
178 {
179         unsigned va[3] = { v0, v1, v2 };
180         uniform3(tag, va);
181 }
182
183 void ProgramData::uniform(Tag tag, float v0, float v1, float v2)
184 {
185         float va[3] = { v0, v1, v2 };
186         uniform3(tag, va);
187 }
188
189 void ProgramData::uniform3(Tag tag, const int *v)
190 {
191         uniform(tag, INT_VEC3, 1, v);
192 }
193
194 void ProgramData::uniform3(Tag tag, const unsigned *v)
195 {
196         uniform(tag, UINT_VEC3, 1, v);
197 }
198
199 void ProgramData::uniform3(Tag tag, const float *v)
200 {
201         uniform(tag, FLOAT_VEC3, 1, v);
202 }
203
204 void ProgramData::uniform(Tag tag, int v0, int v1, int v2, int v3)
205 {
206         int va[4] = { v0, v1, v2, v3 };
207         uniform4(tag, va);
208 }
209
210 void ProgramData::uniform(Tag tag, unsigned v0, unsigned v1, unsigned v2, unsigned v3)
211 {
212         unsigned va[4] = { v0, v1, v2, v3 };
213         uniform4(tag, va);
214 }
215
216 void ProgramData::uniform(Tag tag, float v0, float v1, float v2, float v3)
217 {
218         float va[4] = { v0, v1, v2, v3 };
219         uniform4(tag, va);
220 }
221
222 void ProgramData::uniform(Tag tag, const Color &c)
223 {
224         uniform(tag, c.r, c.g, c.b, c.a);
225 }
226
227 void ProgramData::uniform4(Tag tag, const int *v)
228 {
229         uniform(tag, INT_VEC4, 1, v);
230 }
231
232 void ProgramData::uniform4(Tag tag, const unsigned *v)
233 {
234         uniform(tag, UINT_VEC4, 1, v);
235 }
236
237 void ProgramData::uniform4(Tag tag, const float *v)
238 {
239         uniform(tag, FLOAT_VEC4, 1, v);
240 }
241
242 void ProgramData::uniform_matrix2(Tag tag, const float *v)
243 {
244         uniform(tag, FLOAT_MAT2, 1, v);
245 }
246
247 void ProgramData::uniform_matrix3x2(Tag tag, const float *v)
248 {
249         uniform(tag, FLOAT_MAT3x2, 1, v);
250 }
251
252 void ProgramData::uniform_matrix4x2(Tag tag, const float *v)
253 {
254         uniform(tag, FLOAT_MAT4x2, 1, v);
255 }
256
257 void ProgramData::uniform_matrix2x3(Tag tag, const float *v)
258 {
259         uniform(tag, FLOAT_MAT2x3, 1, v);
260 }
261
262 void ProgramData::uniform_matrix3(Tag tag, const float *v)
263 {
264         uniform(tag, FLOAT_MAT3, 1, v);
265 }
266
267 void ProgramData::uniform_matrix4x3(Tag tag, const float *v)
268 {
269         uniform(tag, FLOAT_MAT4x3, 1, v);
270 }
271
272 void ProgramData::uniform_matrix2x4(Tag tag, const float *v)
273 {
274         uniform(tag, FLOAT_MAT2x4, 1, v);
275 }
276
277 void ProgramData::uniform_matrix3x4(Tag tag, const float *v)
278 {
279         uniform(tag, FLOAT_MAT3x4, 1, v);
280 }
281
282 void ProgramData::uniform(Tag tag, const Matrix &m)
283 {
284         uniform_matrix4(tag, m.data());
285 }
286
287 void ProgramData::uniform_matrix4(Tag tag, const float *v)
288 {
289         uniform(tag, FLOAT_MAT4, 1, v);
290 }
291
292 void ProgramData::uniform_array(Tag tag, unsigned n, const int *v)
293 {
294         uniform(tag, INT, n, v);
295 }
296
297 void ProgramData::uniform_array(Tag tag, unsigned n, const unsigned *v)
298 {
299         uniform(tag, UNSIGNED_INT, n, v);
300 }
301
302 void ProgramData::uniform_array(Tag tag, unsigned n, const float *v)
303 {
304         uniform(tag, FLOAT, n, v);
305 }
306
307 void ProgramData::uniform1_array(Tag tag, unsigned n, const int *v)
308 {
309         uniform(tag, INT, n, v);
310 }
311
312 void ProgramData::uniform1_array(Tag tag, unsigned n, const unsigned *v)
313 {
314         uniform(tag, UNSIGNED_INT, n, v);
315 }
316
317 void ProgramData::uniform1_array(Tag tag, unsigned n, const float *v)
318 {
319         uniform(tag, FLOAT, n, v);
320 }
321
322 void ProgramData::uniform2_array(Tag tag, unsigned n, const int *v)
323 {
324         uniform(tag, INT_VEC2, n, v);
325 }
326
327 void ProgramData::uniform2_array(Tag tag, unsigned n, const unsigned *v)
328 {
329         uniform(tag, UINT_VEC2, n, v);
330 }
331
332 void ProgramData::uniform2_array(Tag tag, unsigned n, const float *v)
333 {
334         uniform(tag, FLOAT_VEC2, n, v);
335 }
336
337 void ProgramData::uniform3_array(Tag tag, unsigned n, const int *v)
338 {
339         uniform(tag, INT_VEC3, n, v);
340 }
341
342 void ProgramData::uniform3_array(Tag tag, unsigned n, const unsigned *v)
343 {
344         uniform(tag, INT_VEC3, n, v);
345 }
346
347 void ProgramData::uniform3_array(Tag tag, unsigned n, const float *v)
348 {
349         uniform(tag, FLOAT_VEC3, n, v);
350 }
351
352 void ProgramData::uniform4_array(Tag tag, unsigned n, const int *v)
353 {
354         uniform(tag, INT_VEC4, n, v);
355 }
356
357 void ProgramData::uniform4_array(Tag tag, unsigned n, const unsigned *v)
358 {
359         uniform(tag, UINT_VEC4, n, v);
360 }
361
362 void ProgramData::uniform4_array(Tag tag, unsigned n, const float *v)
363 {
364         uniform(tag, FLOAT_VEC4, n, v);
365 }
366
367 void ProgramData::uniform_matrix2_array(Tag tag, unsigned n, const float *v)
368 {
369         uniform(tag, FLOAT_MAT2, n, v);
370 }
371
372 void ProgramData::uniform_matrix3x2_array(Tag tag, unsigned n, const float *v)
373 {
374         uniform(tag, FLOAT_MAT3x2, n, v);
375 }
376
377 void ProgramData::uniform_matrix4x2_array(Tag tag, unsigned n, const float *v)
378 {
379         uniform(tag, FLOAT_MAT4x2, n, v);
380 }
381
382 void ProgramData::uniform_matrix2x3_array(Tag tag, unsigned n, const float *v)
383 {
384         uniform(tag, FLOAT_MAT2x3, n, v);
385 }
386
387 void ProgramData::uniform_matrix3_array(Tag tag, unsigned n, const float *v)
388 {
389         uniform(tag, FLOAT_MAT3, n, v);
390 }
391
392 void ProgramData::uniform_matrix4x3_array(Tag tag, unsigned n, const float *v)
393 {
394         uniform(tag, FLOAT_MAT4x3, n, v);
395 }
396
397 void ProgramData::uniform_matrix2x4_array(Tag tag, unsigned n, const float *v)
398 {
399         uniform(tag, FLOAT_MAT2x4, n, v);
400 }
401
402 void ProgramData::uniform_matrix3x4_array(Tag tag, unsigned n, const float *v)
403 {
404         uniform(tag, FLOAT_MAT3x4, n, v);
405 }
406
407 void ProgramData::uniform_matrix4_array(Tag tag, unsigned n, const float *v)
408 {
409         uniform(tag, FLOAT_MAT4, n, v);
410 }
411
412 void ProgramData::remove_uniform(Tag tag)
413 {
414         auto i = lower_bound_member(uniforms, tag, &TaggedUniform::tag);
415         if(i==uniforms.end() || i->tag!=tag)
416                 return;
417
418         uniform_data.erase(uniform_data.begin()+i->data_offset, uniform_data.begin()+i->data_offset+i->data_size);
419         for(TaggedUniform &u: uniforms)
420                 if(u.data_offset>i->data_offset)
421                         u.data_offset -= i->data_size;
422         uniforms.erase(i);
423
424         mark_dirty(ALL_ONES);
425 }
426
427 vector<Tag> ProgramData::get_uniform_tags() const
428 {
429         vector<Tag> tags;
430         tags.reserve(uniforms.size());
431         for(const TaggedUniform &u: uniforms)
432                 tags.push_back(u.tag);
433         return tags;
434 }
435
436 void ProgramData::copy_uniform(const ProgramData &source, Tag tag)
437 {
438         int i = source.find_uniform_index(tag);
439         if(i<0)
440                 throw key_error(tag);
441         const TaggedUniform &tu = source.uniforms[i];
442         uniform(tag, tu.type, tu.array_size, source.uniform_data.data()+tu.data_offset);
443 }
444
445 void ProgramData::copy_uniforms(const ProgramData &source)
446 {
447         for(const TaggedUniform &u: source.uniforms)
448                 uniform(u.tag, u.type, u.array_size, source.uniform_data.data()+u.data_offset);
449 }
450
451 int ProgramData::find_uniform_index(Tag tag) const
452 {
453         auto i = lower_bound_member(uniforms, tag, &TaggedUniform::tag);
454         return ((i!=uniforms.end() && i->tag==tag) ? i-uniforms.begin() : -1);
455 }
456
457 vector<ProgramData::ProgramBlock>::iterator ProgramData::get_program(const Program &prog) const
458 {
459         ReflectData::LayoutHash prog_hash = prog.get_uniform_layout_hash();
460         auto i = lower_bound_member(programs, prog_hash, &ProgramBlock::prog_hash);
461         if(i!=programs.end() && i->prog_hash==prog_hash)
462                 return i;
463
464         const vector<ReflectData::UniformBlockInfo> &block_infos = prog.get_uniform_blocks();
465         unsigned index = i-programs.begin();
466         programs.insert(i, 1+block_infos.size(), ProgramBlock(prog_hash));
467
468         /* Block indices may change if new shared blocks need to be inserted.  Store
469         the hashes so they can be matched up later. */
470         vector<ReflectData::LayoutHash> block_hashes;
471         block_hashes.reserve(programs.size());
472         for(const ProgramBlock &b: programs)
473                 block_hashes.push_back(b.block_index>=0 ? blocks[b.block_index].block_hash : 0);
474
475         for(unsigned j=0; j<block_infos.size(); ++j)
476         {
477                 const ReflectData::UniformBlockInfo &info = block_infos[j];
478                 block_hashes[index+1+j] = info.layout_hash;
479                 programs[index+1+j].bind_point = info.bind_point;
480
481                 auto k = lower_bound_member(blocks, info.layout_hash, &SharedBlock::block_hash);
482                 if(k==blocks.end() || k->block_hash!=info.layout_hash)
483                 {
484                         k = blocks.insert(k, SharedBlock(info.layout_hash));
485                         update_block_uniform_indices(*k, info);
486                 }
487         }
488
489         /* Reassign shared block indices from the stored hashes. */
490         for(unsigned j=0; j<programs.size(); ++j)
491         {
492                 unsigned hash = block_hashes[j];
493                 if(hash)
494                 {
495                         auto k = lower_bound_member(blocks, hash, &SharedBlock::block_hash);
496                         programs[j].block_index = k-blocks.begin();
497                 }
498                 else
499                         programs[j].block_index = -1;
500         }
501
502         return programs.begin()+index;
503 }
504
505 void ProgramData::update_block_uniform_indices(SharedBlock &block, const ReflectData::UniformBlockInfo &info) const
506 {
507         uint8_t *indices = block.indices.values;
508         if(info.uniforms.size()>16)
509         {
510                 if(block.indices.type_flag==0xFD)
511                 {
512                         block.indices.dynamic.values = new uint8_t[info.uniforms.size()];
513                         block.indices.type_flag = 0xFE;
514                 }
515                 indices = block.indices.dynamic.values;
516         }
517
518         bool any_missing = false;
519
520         block.used = 0;
521         for(unsigned i=0; i<info.uniforms.size(); ++i)
522         {
523                 int j = find_uniform_index(info.uniforms[i]->tag);
524                 if(j>=0)
525                 {
526                         indices[i] = j;
527                         if(static_cast<unsigned>(j)<MASK_BITS)
528                                 block.used |= 1<<j;
529                 }
530                 else
531                 {
532                         indices[i] = 0xFF;
533                         any_missing = true;
534                 }
535         }
536
537         if(block.used && any_missing && info.bind_point>=0)
538         {
539 #ifdef DEBUG
540                 IO::print(IO::cerr, "Warning: not all uniforms for block %s are present\n", info.name);
541 #else
542                 throw incomplete_uniform_block(info.name);
543 #endif
544         }
545
546         block.dirty = block.used;
547
548         if(block.used && !block.block)
549         {
550                 block.block = new UniformBlock(info);
551                 if(info.bind_point>=0)
552                 {
553                         if(!buffer)
554                         {
555                                 buffer = new Buffer();
556
557 #ifdef DEBUG
558                                 if(!debug_name.empty())
559                                         buffer->set_debug_name(debug_name);
560 #endif
561                         }
562
563                         block.block->use_buffer(buffer, last_buffer_block);
564                         last_buffer_block = block.block;
565                 }
566         }
567 }
568
569 void ProgramData::update_block(SharedBlock &block, const ReflectData::UniformBlockInfo &info) const
570 {
571         const uint8_t *indices = block.get_uniform_indices();
572         for(unsigned i=0; i<info.uniforms.size(); ++i)
573         {
574                 if(is_image(info.uniforms[i]->type))
575                         ;  // Temporarily ignore deprecated use of sampler uniforms in ProgramData
576                 else if(indices[i]!=0xFF)
577                 {
578                         const TaggedUniform &tu = uniforms[indices[i]];
579                         block.block->store(*info.uniforms[i], tu.array_size, uniform_data.data()+tu.data_offset);
580                 }
581         }
582 }
583
584 vector<ProgramData::ProgramBlock>::const_iterator ProgramData::prepare_program(const Program &prog) const
585 {
586         UniformBlock *old_last_block = last_buffer_block;
587         auto prog_begin = get_program(prog);
588
589         Mask force_dirty = (dirty==ALL_ONES ? ALL_ONES : 0U);
590         Mask affected = (dirty&prog_begin->masks.used) | force_dirty;
591         if(affected|prog_begin->masks.dirty)
592         {
593                 /* If the global dirty flag affects this program, add it to per-block and
594                 per-program dirty flags and clear the global flag.  A previously unseen
595                 program will cause this to happen if there's any dirty uniforms. */
596                 if(affected)
597                 {
598                         for(SharedBlock &b: blocks)
599                                 b.dirty |= (dirty&b.used) | force_dirty;
600                         for(ProgramBlock &b: programs)
601                                 if(b.block_index<0)
602                                         b.masks.dirty |= (dirty&b.masks.used) | force_dirty;
603                         dirty = 0;
604                 }
605
606                 const vector<ReflectData::UniformBlockInfo> &block_infos = prog.get_uniform_blocks();
607
608                 if(prog_begin->masks.dirty==ALL_ONES)
609                 {
610                         /* The set of uniforms has changed since this program was last used.
611                         Refresh uniform indices within the program's blocks. */
612                         prog_begin->masks.used = 0;
613                         auto j = prog_begin+1;
614                         for(const ReflectData::UniformBlockInfo &b: block_infos)
615                         {
616                                 SharedBlock &shared = blocks[j->block_index];
617                                 if(shared.dirty==ALL_ONES)
618                                         update_block_uniform_indices(shared, b);
619                                 prog_begin->masks.used |= shared.used;
620                                 j->block = (shared.used ? shared.block : 0);
621                                 ++j;
622                         }
623                 }
624
625                 // Update the contents of all dirty blocks.
626                 bool buffered_blocks_updated = false;
627                 auto j = prog_begin+1;
628                 for(const ReflectData::UniformBlockInfo &b: block_infos)
629                 {
630                         SharedBlock &shared = blocks[j->block_index];
631                         if(shared.dirty)
632                         {
633                                 update_block(shared, b);
634                                 shared.dirty = 0;
635                                 buffered_blocks_updated |= (j->bind_point>=0);
636                         }
637                         ++j;
638                 }
639
640                 prog_begin->masks.dirty = 0;
641
642                 if(last_buffer_block!=old_last_block)
643                 {
644                         unsigned required_size = last_buffer_block->get_required_buffer_size();
645                         if(last_buffer_block->get_required_buffer_size()>buffer->get_size())
646                         {
647                                 if(buffer->get_size()>0)
648                                 {
649                                         delete buffer;
650                                         buffer = new Buffer();
651                                         last_buffer_block->change_buffer(buffer);
652
653 #ifdef DEBUG
654                                         if(!debug_name.empty())
655                                                 buffer->set_debug_name(debug_name);
656 #endif
657                                 }
658
659                                 buffer->storage(required_size, STREAMING);
660                         }
661                 }
662         }
663
664         return prog_begin;
665 }
666
667 void ProgramData::apply(const Program &prog, PipelineState &state) const
668 {
669         auto prog_begin = prepare_program(prog);
670         ReflectData::LayoutHash prog_hash = prog_begin->prog_hash;
671         for(auto i=prog_begin+1; (i!=programs.end() && i->prog_hash==prog_hash); ++i)
672                 if(i->block)
673                 {
674                         state.set_uniform_block(i->bind_point, i->block);
675                         if(i->bind_point>=0)
676                                 i->block->refresh();
677                 }
678 }
679
680 void ProgramData::set_debug_name(const string &name)
681 {
682 #ifdef DEBUG
683         debug_name = name;
684         if(buffer)
685                 buffer->set_debug_name(name);
686 #else
687         (void)name;
688 #endif
689 }
690
691
692 ProgramData::SharedBlock::SharedBlock(ReflectData::LayoutHash h):
693         block_hash(h),
694         used(0),
695         dirty(0),
696         block(0)
697 {
698         indices.type_flag = 0xFD;
699 }
700
701 const uint8_t *ProgramData::SharedBlock::get_uniform_indices() const
702 {
703         return (indices.type_flag==0xFE ? indices.dynamic.values : indices.values);
704 }
705
706
707 ProgramData::ProgramBlock::ProgramBlock(ReflectData::LayoutHash h):
708         prog_hash(h),
709         bind_point(-1),
710         block_index(-1)
711 {
712         masks.used = ALL_ONES;
713         masks.dirty = ALL_ONES;
714 }
715
716
717 ProgramData::Loader::Loader(ProgramData &pd):
718         DataFile::ObjectLoader<ProgramData>(pd)
719 {
720         add("uniform", &Loader::uniform1i);
721         add("uniform1i", &Loader::uniform1i);
722         add("uniform", &Loader::uniform1f);
723         add("uniform1f", &Loader::uniform1f);
724         add("uniform", &Loader::uniform2i);
725         add("uniform2i", &Loader::uniform2i);
726         add("uniform", &Loader::uniform2f);
727         add("uniform2f", &Loader::uniform2f);
728         add("uniform", &Loader::uniform3i);
729         add("uniform3i", &Loader::uniform3i);
730         add("uniform", &Loader::uniform3f);
731         add("uniform3f", &Loader::uniform3f);
732         add("uniform", &Loader::uniform4i);
733         add("uniform4i", &Loader::uniform4i);
734         add("uniform", &Loader::uniform4f);
735         add("uniform4f", &Loader::uniform4f);
736         add("uniform1i_array", &Loader::uniform1i_array);
737         add("uniform1f_array", &Loader::uniform1f_array);
738         add("uniform2f_array", &Loader::uniform2f_array);
739         add("uniform3f_array", &Loader::uniform3f_array);
740         add("uniform4f_array", &Loader::uniform4f_array);
741         add("uniform_array", &Loader::uniform_array);
742 }
743
744 void ProgramData::Loader::uniform1i(const string &n, int v)
745 {
746         obj.uniform(n, v);
747 }
748
749 void ProgramData::Loader::uniform1f(const string &n, float v)
750 {
751         obj.uniform(n, v);
752 }
753
754 void ProgramData::Loader::uniform2i(const string &n, int v0, int v1)
755 {
756         obj.uniform(n, v0, v1);
757 }
758
759 void ProgramData::Loader::uniform2f(const string &n, float v0, float v1)
760 {
761         obj.uniform(n, v0, v1);
762 }
763
764 void ProgramData::Loader::uniform3i(const string &n, int v0, int v1, int v2)
765 {
766         obj.uniform(n, v0, v1, v2);
767 }
768
769 void ProgramData::Loader::uniform3f(const string &n, float v0, float v1, float v2)
770 {
771         obj.uniform(n, v0, v1, v2);
772 }
773
774 void ProgramData::Loader::uniform4i(const string &n, int v0, int v1, int v2, int v3)
775 {
776         obj.uniform(n, v0, v1, v2, v3);
777 }
778
779 void ProgramData::Loader::uniform4f(const string &n, float v0, float v1, float v2, float v3)
780 {
781         obj.uniform(n, v0, v1, v2, v3);
782 }
783
784 void ProgramData::Loader::uniform_array_(const string &n, DataType t, unsigned e)
785 {
786         ArrayLoader ldr(t, e);
787         load_sub_with(ldr);
788         unsigned size = ldr.get_size();
789         if(!size)
790                 throw logic_error("empty uniform array");
791
792         DataType type = ldr.get_data_type();
793         unsigned elem_size = ldr.get_element_size();
794         if(type==INT)
795         {
796                 const int *data = reinterpret_cast<const int *>(ldr.get_data());
797                 if(elem_size==1)
798                         obj.uniform1_array(n, size, data);
799                 else if(elem_size==2)
800                         obj.uniform2_array(n, size, data);
801                 else if(elem_size==3)
802                         obj.uniform3_array(n, size, data);
803                 else if(elem_size==4)
804                         obj.uniform4_array(n, size, data);
805                 else
806                         throw logic_error("unsupported combination of array type and element size");
807         }
808         else if(type==FLOAT)
809         {
810                 const float *data = reinterpret_cast<const float *>(ldr.get_data());
811                 if(elem_size==1)
812                         obj.uniform1_array(n, size, data);
813                 else if(elem_size==2)
814                         obj.uniform2_array(n, size, data);
815                 else if(elem_size==3)
816                         obj.uniform3_array(n, size, data);
817                 else if(elem_size==4)
818                         obj.uniform4_array(n, size, data);
819                 else
820                         throw logic_error("unsupported combination of array type and element size");
821         }
822         else
823                 throw logic_error("unsupported array type");
824 }
825
826 void ProgramData::Loader::uniform1i_array(const string &n)
827 {
828         uniform_array_(n, INT, 1);
829 }
830
831 void ProgramData::Loader::uniform1f_array(const string &n)
832 {
833         uniform_array_(n, FLOAT, 1);
834 }
835
836 void ProgramData::Loader::uniform2i_array(const string &n)
837 {
838         uniform_array_(n, INT, 2);
839 }
840
841 void ProgramData::Loader::uniform2f_array(const string &n)
842 {
843         uniform_array_(n, FLOAT, 2);
844 }
845
846 void ProgramData::Loader::uniform3i_array(const string &n)
847 {
848         uniform_array_(n, INT, 3);
849 }
850
851 void ProgramData::Loader::uniform3f_array(const string &n)
852 {
853         uniform_array_(n, FLOAT, 3);
854 }
855
856 void ProgramData::Loader::uniform4i_array(const string &n)
857 {
858         uniform_array_(n, INT, 4);
859 }
860
861 void ProgramData::Loader::uniform4f_array(const string &n)
862 {
863         uniform_array_(n, FLOAT, 4);
864 }
865
866 void ProgramData::Loader::uniform_array(const string &n)
867 {
868         uniform_array_(n, static_cast<DataType>(0), 0);
869 }
870
871
872 ProgramData::ArrayLoader::ArrayLoader(DataType t, unsigned e):
873         type(t),
874         element_size(e)
875 {
876         add("uniform", &ArrayLoader::uniform1i);
877         add("uniform1i", &ArrayLoader::uniform1i);
878         add("uniform", &ArrayLoader::uniform1f);
879         add("uniform1f", &ArrayLoader::uniform1f);
880         add("uniform", &ArrayLoader::uniform2f);
881         add("uniform2f", &ArrayLoader::uniform2f);
882         add("uniform", &ArrayLoader::uniform3f);
883         add("uniform3f", &ArrayLoader::uniform3f);
884         add("uniform", &ArrayLoader::uniform4f);
885         add("uniform4f", &ArrayLoader::uniform4f);
886 }
887
888 void ProgramData::ArrayLoader::uniform(DataType t, unsigned e, const void *v)
889 {
890         if(element_size && (t!=type || e!=element_size))
891                 throw logic_error("heterogeneous array contents");
892
893         if(!element_size)
894         {
895                 type = t;
896                 element_size = e;
897         }
898
899         const char *cv = reinterpret_cast<const char *>(v);
900         data.insert(data.end(), cv, cv+element_size*4);
901 }
902
903 void ProgramData::ArrayLoader::uniform1i(int v)
904 {
905         uniform(INT, 1, &v);
906 }
907
908 void ProgramData::ArrayLoader::uniform1f(float v)
909 {
910         uniform(FLOAT, 1, &v);
911 }
912
913 void ProgramData::ArrayLoader::uniform2i(int v0, int v1)
914 {
915         int va[2] = { v0, v1 };
916         uniform(INT, 2, va);
917 }
918
919 void ProgramData::ArrayLoader::uniform2f(float v0, float v1)
920 {
921         float va[2] = { v0, v1 };
922         uniform(FLOAT, 2, va);
923 }
924
925 void ProgramData::ArrayLoader::uniform3i(int v0, int v1, int v2)
926 {
927         int va[3] = { v0, v1, v2 };
928         uniform(INT, 3, va);
929 }
930
931 void ProgramData::ArrayLoader::uniform3f(float v0, float v1, float v2)
932 {
933         float va[3] = { v0, v1, v2 };
934         uniform(FLOAT, 3, va);
935 }
936
937 void ProgramData::ArrayLoader::uniform4i(int v0, int v1, int v2, int v3)
938 {
939         int va[4] = { v0, v1, v2, v3 };
940         uniform(INT, 4, va);
941 }
942
943 void ProgramData::ArrayLoader::uniform4f(float v0, float v1, float v2, float v3)
944 {
945         float va[4] = { v0, v1, v2, v3 };
946         uniform(FLOAT, 4, va);
947 }
948
949 } // namespace GL
950 } // namespace Msp