]> git.tdb.fi Git - libs/gl.git/blob - source/programdata.cpp
Allow setting uniform values using a Uniform object
[libs/gl.git] / source / programdata.cpp
1 #include <msp/gl/extensions/arb_direct_state_access.h>
2 #include "buffer.h"
3 #include "color.h"
4 #include "error.h"
5 #include "matrix.h"
6 #include "program.h"
7 #include "programdata.h"
8 #include "uniform.h"
9 #include "uniformblock.h"
10 #include "vector.h"
11
12 using namespace std;
13
14 namespace Msp {
15 namespace GL {
16
17 ProgramData::ProgramData(const Program *p):
18         tied_program(p),
19         last_block(0),
20         buffer(0),
21         dirty(0)
22 { }
23
24 // Blocks are intentionally left uncopied
25 ProgramData::ProgramData(const ProgramData &other):
26         tied_program(0),
27         uniform_slots(other.uniform_slots),
28         uniforms(other.uniforms),
29         last_block(0),
30         buffer(0),
31         dirty(0)
32 {
33         for(vector<Uniform *>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
34                 *i = (*i)->clone();
35 }
36
37 ProgramData &ProgramData::operator=(const ProgramData &other)
38 {
39         for(vector<Uniform *>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
40                 delete *i;
41         uniforms.clear();
42
43         tied_program = other.tied_program;
44
45         uniform_slots = other.uniform_slots;
46         for(vector<Uniform *>::const_iterator i=other.uniforms.begin(); i!=other.uniforms.end(); ++i)
47                 uniforms.push_back((*i)->clone());
48
49         for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i)
50                 delete i->second.block;
51         programs.clear();
52
53         last_block = 0;
54         buffer = 0;
55         dirty = 0;
56
57         return *this;
58 }
59
60 ProgramData::~ProgramData()
61 {
62         for(vector<Uniform *>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
63                 delete *i;
64         for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i)
65                 delete i->second.block;
66         delete buffer;
67 }
68
69 void ProgramData::uniform(const string &name, Uniform *uni)
70 {
71         try
72         {
73                 if(tied_program)
74                         tied_program->get_uniform_info(name);
75                 else if(name[name.size()-1]==']')
76                         throw invalid_argument("ProgramData::uniform");
77         }
78         catch(...)
79         {
80                 delete uni;
81                 throw;
82         }
83
84         SlotMap::iterator i = uniform_slots.find(name);
85         if(i!=uniform_slots.end())
86         {
87                 Uniform *&slot = uniforms[i->second];
88                 /* UniformBlock does not copy the uniforms, so existing default blocks
89                 will be left with stale pointers.  This is not a problem as long as no
90                 one stores pointers to the blocks and expects them to stay valid. */
91                 delete slot;
92                 slot = uni;
93
94                 if(i->second<MASK_BITS)
95                         dirty |= 1<<i->second;
96                 else  // Force a full update if the mask isn't wide enough
97                         dirty = ALL_ONES;
98         }
99         else
100         {
101                 uniform_slots[name] = uniforms.size();
102                 uniforms.push_back(uni);
103                 dirty = ALL_ONES;
104         }
105 }
106
107 void ProgramData::uniform(const string &name, const Uniform &u)
108 {
109         uniform(name, u.clone());
110 }
111
112 void ProgramData::uniform(const string &name, int v)
113 {
114         uniform(name, new Uniform1i(v));
115 }
116
117 void ProgramData::uniform(const string &name, float v)
118 {
119         uniform(name, new Uniform1f(v));
120 }
121
122 void ProgramData::uniform(const string &name, int v0, int v1)
123 {
124         int va[2] = { v0, v1 };
125         uniform2(name, va);
126 }
127
128 void ProgramData::uniform(const string &name, float v0, float v1)
129 {
130         float va[2] = { v0, v1 };
131         uniform2(name, va);
132 }
133
134 void ProgramData::uniform2(const string &name, const int *v)
135 {
136         uniform(name, new Uniform2i(v));
137 }
138
139 void ProgramData::uniform2(const string &name, const float *v)
140 {
141         uniform(name, new Uniform2f(v));
142 }
143
144 void ProgramData::uniform(const string &name, int v0, int v1, int v2)
145 {
146         int va[3] = { v0, v1, v2 };
147         uniform3(name, va);
148 }
149
150 void ProgramData::uniform(const string &name, float v0, float v1, float v2)
151 {
152         float va[3] = { v0, v1, v2 };
153         uniform3(name, va);
154 }
155
156 void ProgramData::uniform(const string &name, const Vector3 &v)
157 {
158         uniform(name, v.x, v.y, v.z);
159 }
160
161 void ProgramData::uniform3(const string &name, const int *v)
162 {
163         uniform(name, new Uniform3i(v));
164 }
165
166 void ProgramData::uniform3(const string &name, const float *v)
167 {
168         uniform(name, new Uniform3f(v));
169 }
170
171 void ProgramData::uniform(const string &name, int v0, int v1, int v2, int v3)
172 {
173         int va[4] = { v0, v1, v2, v3 };
174         uniform4(name, va);
175 }
176
177 void ProgramData::uniform(const string &name, float v0, float v1, float v2, float v3)
178 {
179         float va[4] = { v0, v1, v2, v3 };
180         uniform4(name, va);
181 }
182
183 void ProgramData::uniform(const string &name, const Vector4 &v)
184 {
185         uniform(name, v.x, v.y, v.z, v.w);
186 }
187
188 void ProgramData::uniform(const string &name, const Color &c)
189 {
190         uniform(name, c.r, c.g, c.b, c.a);
191 }
192
193 void ProgramData::uniform4(const string &name, const int *v)
194 {
195         uniform(name, new Uniform4i(v));
196 }
197
198 void ProgramData::uniform4(const string &name, const float *v)
199 {
200         uniform(name, new Uniform4f(v));
201 }
202
203 void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 2, 2> &m)
204 {
205         uniform_matrix2(name, &m(0, 0));
206 }
207
208 void ProgramData::uniform_matrix2(const string &name, const float *v)
209 {
210         uniform(name, new UniformMatrix2x2f(v));
211 }
212
213 void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 3, 3> &m)
214 {
215         uniform_matrix3(name, &m(0, 0));
216 }
217
218 void ProgramData::uniform_matrix3(const string &name, const float *v)
219 {
220         uniform(name, new UniformMatrix3x3f(v));
221 }
222
223 void ProgramData::uniform(const string &name, const Matrix &m)
224 {
225         uniform_matrix4(name, m.data());
226 }
227
228 void ProgramData::uniform_matrix4(const string &name, const float *v)
229 {
230         uniform(name, new UniformMatrix4x4f(v));
231 }
232
233 void ProgramData::uniform1_array(const string &name, unsigned n, const int *v)
234 {
235         uniform(name, new UniformArray<Uniform1i>(n, v));
236 }
237
238 void ProgramData::uniform1_array(const string &name, unsigned n, const float *v)
239 {
240         uniform(name, new UniformArray<Uniform1f>(n, v));
241 }
242
243 void ProgramData::uniform2_array(const string &name, unsigned n, const int *v)
244 {
245         uniform(name, new UniformArray<Uniform2i>(n, v));
246 }
247
248 void ProgramData::uniform2_array(const string &name, unsigned n, const float *v)
249 {
250         uniform(name, new UniformArray<Uniform2f>(n, v));
251 }
252
253 void ProgramData::uniform3_array(const string &name, unsigned n, const int *v)
254 {
255         uniform(name, new UniformArray<Uniform3i>(n, v));
256 }
257
258 void ProgramData::uniform3_array(const string &name, unsigned n, const float *v)
259 {
260         uniform(name, new UniformArray<Uniform3f>(n, v));
261 }
262
263 void ProgramData::uniform4_array(const string &name, unsigned n, const int *v)
264 {
265         uniform(name, new UniformArray<Uniform4i>(n, v));
266 }
267
268 void ProgramData::uniform4_array(const string &name, unsigned n, const float *v)
269 {
270         uniform(name, new UniformArray<Uniform4f>(n, v));
271 }
272
273 void ProgramData::uniform_matrix4_array(const string &name, unsigned n, const float *v)
274 {
275         uniform(name, new UniformArray<UniformMatrix4x4f>(n, v));
276 }
277
278 void ProgramData::remove_uniform(const string &name)
279 {
280         SlotMap::iterator i = uniform_slots.find(name);
281         if(i!=uniform_slots.end())
282         {
283                 vector<Uniform *>::iterator j = uniforms.begin()+i->second;
284                 delete *j;
285                 uniforms.erase(j);
286
287                 for(SlotMap::iterator k=uniform_slots.begin(); k!=uniform_slots.end(); ++k)
288                         if(k->second>i->second)
289                                 --k->second;
290
291                 uniform_slots.erase(i);
292
293                 dirty = ALL_ONES;
294         }
295 }
296
297 unsigned ProgramData::compute_slot_mask(const Program::UniformBlockInfo &block) const
298 {
299         unsigned mask = 0;
300         for(vector<const Program::UniformInfo *>::const_iterator i=block.uniforms.begin(); i!=block.uniforms.end(); ++i)
301         {
302                 SlotMap::const_iterator j = uniform_slots.find((*i)->name);
303                 /* TODO issue a warning (or even error?) either here or in update_block
304                 if all uniforms for a buffer-backed block are not found */
305                 if(j!=uniform_slots.end() && j->second<MASK_BITS)
306                         mask |= 1<<j->second;
307         }
308
309         return mask;
310 }
311
312 void ProgramData::update_block(UniformBlock &block, const Program::UniformBlockInfo &info) const
313 {
314         for(vector<const Program::UniformInfo *>::const_iterator i=info.uniforms.begin(); i!=info.uniforms.end(); ++i)
315         {
316                 SlotMap::const_iterator j = uniform_slots.find((*i)->name);
317                 if(j!=uniform_slots.end())
318                         block.attach(**i, *uniforms[j->second]);
319         }
320 }
321
322 ProgramData::SharedBlock *ProgramData::get_shared_block(const Program::UniformBlockInfo &info) const
323 {
324         BlockMap::iterator i = blocks.find(info.layout_hash);
325         if(i==blocks.end())
326         {
327                 unsigned used = compute_slot_mask(info);
328                 if(!used)
329                         return 0;
330
331                 UniformBlock *block;
332                 if(info.bind_point>=0)
333                 {
334                         if(!buffer)
335                         {
336                                 buffer = new Buffer(UNIFORM_BUFFER);
337                                 buffer->set_usage(STREAM_DRAW);
338                         }
339
340                         block = new UniformBlock(info.data_size);
341                         block->use_buffer(buffer, last_block);
342                         last_block = block;
343                 }
344                 else
345                         block = new UniformBlock;
346
347                 i = blocks.insert(BlockMap::value_type(info.layout_hash, SharedBlock(used, block))).first;
348         }
349
350         return &i->second;
351 }
352
353 void ProgramData::apply() const
354 {
355         const Program *prog = Program::current();
356         if(!prog)
357                 throw invalid_operation("ProgramData::apply");
358
359         Program::LayoutHash layout = prog->get_uniform_layout_hash();
360         ProgramUniforms &pu = programs[layout];
361
362         Mask force_dirty = (dirty==ALL_ONES ? ALL_ONES : 0U);
363         Mask affected = (dirty&pu.used) | force_dirty;
364         if(affected|pu.dirty)
365         {
366                 /* If the global dirty flag affects this program, add it to per-program
367                 dirty flags and clear the global flag.  A previously unseen program will
368                 always cause this to happen. */
369                 if(affected)
370                 {
371                         for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i)
372                                 i->second.dirty |= (dirty&i->second.used) | force_dirty;
373                         for(ProgramMap::iterator i=programs.begin(); i!=programs.end(); ++i)
374                                 i->second.dirty |= (dirty&i->second.used) | force_dirty;
375                         dirty = 0;
376                 }
377
378                 const Program::UniformBlockMap &prog_blocks = prog->get_uniform_blocks();
379
380                 if(pu.dirty==ALL_ONES)
381                 {
382                         /* The set of uniforms has changed since this program was last used.
383                         Regenerate the list of uniform blocks. */
384                         pu.blocks.clear();
385                         pu.blocks.reserve(prog_blocks.size());
386
387                         pu.used = 0;
388                         for(Program::UniformBlockMap::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i)
389                         {
390                                 SharedBlock *shared = get_shared_block(i->second);
391                                 if(shared)
392                                 {
393                                         if(shared->dirty==ALL_ONES)
394                                                 shared->used = compute_slot_mask(i->second);
395                                         pu.used |= shared->used;
396                                 }
397
398                                 pu.blocks.push_back(ProgramBlock(i->second.bind_point, shared));
399                         }
400                 }
401
402                 // Update the contents of all dirty blocks.
403                 bool buffered_blocks_updated = false;
404                 std::vector<ProgramBlock>::iterator j = pu.blocks.begin();
405                 for(Program::UniformBlockMap::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i, ++j)
406                 {
407                         if(!j->shared || !j->shared->dirty)
408                                 continue;
409
410                         update_block(*j->block, i->second);
411                         j->shared->dirty = 0;
412                         buffered_blocks_updated |= (j->bind_point>=0);
413                 }
414
415                 pu.dirty = 0;
416
417                 /* If any blocks stored in the buffer were updated, bind the buffer here
418                 to avoid state thrashing. */
419                 if(buffered_blocks_updated && !ARB_direct_state_access)
420                         buffer->bind();
421         }
422
423         for(vector<ProgramBlock>::iterator i=pu.blocks.begin(); i!=pu.blocks.end(); ++i)
424                 if(i->block)
425                         i->block->apply(i->bind_point);
426 }
427
428
429 ProgramData::SharedBlock::SharedBlock(unsigned u, UniformBlock *b):
430         used(u),
431         dirty(u),
432         block(b)
433 { }
434
435
436 ProgramData::ProgramBlock::ProgramBlock():
437         bind_point(-1),
438         block(0),
439         shared(0)
440 { }
441
442 ProgramData::ProgramBlock::ProgramBlock(int p, SharedBlock *b):
443         bind_point(p),
444         block(b ? b->block : 0),
445         shared(b)
446 { }
447
448
449 ProgramData::ProgramUniforms::ProgramUniforms():
450         used(ALL_ONES),
451         dirty(ALL_ONES)
452 { }
453
454
455 ProgramData::Loader::Loader(ProgramData &pd):
456         DataFile::ObjectLoader<ProgramData>(pd)
457 {
458         add("uniform", &Loader::uniform1i);
459         add("uniform1i", &Loader::uniform1i);
460         add("uniform", &Loader::uniform1f);
461         add("uniform1f", &Loader::uniform1f);
462         add("uniform", &Loader::uniform2i);
463         add("uniform2i", &Loader::uniform2i);
464         add("uniform", &Loader::uniform2f);
465         add("uniform2f", &Loader::uniform2f);
466         add("uniform", &Loader::uniform3i);
467         add("uniform3i", &Loader::uniform3i);
468         add("uniform", &Loader::uniform3f);
469         add("uniform3f", &Loader::uniform3f);
470         add("uniform", &Loader::uniform4i);
471         add("uniform4i", &Loader::uniform4i);
472         add("uniform", &Loader::uniform4f);
473         add("uniform4f", &Loader::uniform4f);
474         add("uniform1i_array", &Loader::uniform1i_array);
475         add("uniform1f_array", &Loader::uniform1f_array);
476         add("uniform2f_array", &Loader::uniform2f_array);
477         add("uniform3f_array", &Loader::uniform3f_array);
478         add("uniform4f_array", &Loader::uniform4f_array);
479         add("uniform_array", &Loader::uniform_array);
480 }
481
482 void ProgramData::Loader::uniform1i(const string &n, int v)
483 {
484         obj.uniform(n, v);
485 }
486
487 void ProgramData::Loader::uniform1f(const string &n, float v)
488 {
489         obj.uniform(n, v);
490 }
491
492 void ProgramData::Loader::uniform2i(const string &n, int v0, int v1)
493 {
494         obj.uniform(n, v0, v1);
495 }
496
497 void ProgramData::Loader::uniform2f(const string &n, float v0, float v1)
498 {
499         obj.uniform(n, v0, v1);
500 }
501
502 void ProgramData::Loader::uniform3i(const string &n, int v0, int v1, int v2)
503 {
504         obj.uniform(n, v0, v1, v2);
505 }
506
507 void ProgramData::Loader::uniform3f(const string &n, float v0, float v1, float v2)
508 {
509         obj.uniform(n, v0, v1, v2);
510 }
511
512 void ProgramData::Loader::uniform4i(const string &n, int v0, int v1, int v2, int v3)
513 {
514         obj.uniform(n, v0, v1, v2, v3);
515 }
516
517 void ProgramData::Loader::uniform4f(const string &n, float v0, float v1, float v2, float v3)
518 {
519         obj.uniform(n, v0, v1, v2, v3);
520 }
521
522 void ProgramData::Loader::uniform_array_(const string &n, DataType t, unsigned e)
523 {
524         ArrayLoader ldr(t, e);
525         load_sub_with(ldr);
526         unsigned size = ldr.get_size();
527         if(!size)
528                 throw logic_error("empty uniform array");
529
530         DataType type = ldr.get_data_type();
531         unsigned elem_size = ldr.get_element_size();
532         if(type==INT)
533         {
534                 const int *data = reinterpret_cast<const int *>(ldr.get_data());
535                 if(elem_size==1)
536                         obj.uniform1_array(n, size, data);
537                 else if(elem_size==2)
538                         obj.uniform2_array(n, size, data);
539                 else if(elem_size==3)
540                         obj.uniform3_array(n, size, data);
541                 else if(elem_size==4)
542                         obj.uniform4_array(n, size, data);
543                 else
544                         throw logic_error("unsupported combination of array type and element size");
545         }
546         else if(type==FLOAT)
547         {
548                 const float *data = reinterpret_cast<const float *>(ldr.get_data());
549                 if(elem_size==1)
550                         obj.uniform1_array(n, size, data);
551                 else if(elem_size==2)
552                         obj.uniform2_array(n, size, data);
553                 else if(elem_size==3)
554                         obj.uniform3_array(n, size, data);
555                 else if(elem_size==4)
556                         obj.uniform4_array(n, size, data);
557                 else
558                         throw logic_error("unsupported combination of array type and element size");
559         }
560         else
561                 throw logic_error("unsupported array type");
562 }
563
564 void ProgramData::Loader::uniform1i_array(const string &n)
565 {
566         uniform_array_(n, INT, 1);
567 }
568
569 void ProgramData::Loader::uniform1f_array(const string &n)
570 {
571         uniform_array_(n, FLOAT, 1);
572 }
573
574 void ProgramData::Loader::uniform2i_array(const string &n)
575 {
576         uniform_array_(n, INT, 2);
577 }
578
579 void ProgramData::Loader::uniform2f_array(const string &n)
580 {
581         uniform_array_(n, FLOAT, 2);
582 }
583
584 void ProgramData::Loader::uniform3i_array(const string &n)
585 {
586         uniform_array_(n, INT, 3);
587 }
588
589 void ProgramData::Loader::uniform3f_array(const string &n)
590 {
591         uniform_array_(n, FLOAT, 3);
592 }
593
594 void ProgramData::Loader::uniform4i_array(const string &n)
595 {
596         uniform_array_(n, INT, 4);
597 }
598
599 void ProgramData::Loader::uniform4f_array(const string &n)
600 {
601         uniform_array_(n, FLOAT, 4);
602 }
603
604 void ProgramData::Loader::uniform_array(const string &n)
605 {
606         uniform_array_(n, static_cast<DataType>(0), 0);
607 }
608
609
610 ProgramData::ArrayLoader::ArrayLoader(DataType t, unsigned e):
611         type(t),
612         element_size(e)
613 {
614         add("uniform", &ArrayLoader::uniform1i);
615         add("uniform1i", &ArrayLoader::uniform1i);
616         add("uniform", &ArrayLoader::uniform1f);
617         add("uniform1f", &ArrayLoader::uniform1f);
618         add("uniform", &ArrayLoader::uniform2f);
619         add("uniform2f", &ArrayLoader::uniform2f);
620         add("uniform", &ArrayLoader::uniform3f);
621         add("uniform3f", &ArrayLoader::uniform3f);
622         add("uniform", &ArrayLoader::uniform4f);
623         add("uniform4f", &ArrayLoader::uniform4f);
624 }
625
626 void ProgramData::ArrayLoader::uniform(DataType t, unsigned e, const void *v)
627 {
628         if(element_size && (t!=type || e!=element_size))
629                 throw logic_error("heterogeneous array contents");
630
631         if(!element_size)
632         {
633                 type = t;
634                 element_size = e;
635         }
636
637         const char *cv = reinterpret_cast<const char *>(v);
638         data.insert(data.end(), cv, cv+element_size*4);
639 }
640
641 void ProgramData::ArrayLoader::uniform1i(int v)
642 {
643         uniform(INT, 1, &v);
644 }
645
646 void ProgramData::ArrayLoader::uniform1f(float v)
647 {
648         uniform(FLOAT, 1, &v);
649 }
650
651 void ProgramData::ArrayLoader::uniform2i(int v0, int v1)
652 {
653         int va[2] = { v0, v1 };
654         uniform(INT, 2, va);
655 }
656
657 void ProgramData::ArrayLoader::uniform2f(float v0, float v1)
658 {
659         float va[2] = { v0, v1 };
660         uniform(FLOAT, 2, va);
661 }
662
663 void ProgramData::ArrayLoader::uniform3i(int v0, int v1, int v2)
664 {
665         int va[3] = { v0, v1, v2 };
666         uniform(INT, 3, va);
667 }
668
669 void ProgramData::ArrayLoader::uniform3f(float v0, float v1, float v2)
670 {
671         float va[3] = { v0, v1, v2 };
672         uniform(FLOAT, 3, va);
673 }
674
675 void ProgramData::ArrayLoader::uniform4i(int v0, int v1, int v2, int v3)
676 {
677         int va[4] = { v0, v1, v2, v3 };
678         uniform(INT, 4, va);
679 }
680
681 void ProgramData::ArrayLoader::uniform4f(float v0, float v1, float v2, float v3)
682 {
683         float va[4] = { v0, v1, v2, v3 };
684         uniform(FLOAT, 4, va);
685 }
686
687 } // namespace GL
688 } // namespace Msp