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