+ const Program *prog = Program::current();
+ if(!prog)
+ throw invalid_operation("ProgramData::apply");
+
+ Program::LayoutHash layout = prog->get_uniform_layout_hash();
+ ProgramUniforms &pu = programs[layout];
+
+ Mask force_dirty = (dirty==ALL_ONES ? ALL_ONES : 0U);
+ Mask affected = (dirty&pu.used) | force_dirty;
+ if(affected|pu.dirty)
+ {
+ /* If the global dirty flag affects this program, add it to per-program
+ dirty flags and clear the global flag. A previously unseen program will
+ always cause this to happen. */
+ if(affected)
+ {
+ for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ i->second.dirty |= (dirty&i->second.used) | force_dirty;
+ for(ProgramMap::iterator i=programs.begin(); i!=programs.end(); ++i)
+ i->second.dirty |= (dirty&i->second.used) | force_dirty;
+ dirty = 0;
+ }
+
+ const Program::UniformBlockMap &prog_blocks = prog->get_uniform_blocks();
+
+ if(pu.dirty==ALL_ONES)
+ {
+ /* The set of uniforms has changed since this program was last used.
+ Regenerate the list of uniform blocks. */
+ pu.blocks.clear();
+ pu.blocks.reserve(prog_blocks.size());
+
+ pu.used = 0;
+ for(Program::UniformBlockMap::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i)
+ {
+ SharedBlock *shared = get_shared_block(i->second);
+ if(shared)
+ {
+ if(shared->dirty==ALL_ONES)
+ shared->used = compute_slot_mask(i->second);
+ pu.used |= shared->used;
+ }
+
+ pu.blocks.push_back(ProgramBlock(i->second.bind_point, shared));
+ }
+ }
+
+ // Update the contents of all dirty blocks.
+ bool buffered_blocks_updated = false;
+ std::vector<ProgramBlock>::iterator j = pu.blocks.begin();
+ for(Program::UniformBlockMap::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i, ++j)
+ {
+ if(!j->shared || !j->shared->dirty)
+ continue;
+
+ update_block(*j->block, i->second);
+ j->shared->dirty = 0;
+ buffered_blocks_updated |= (j->bind_point>=0);
+ }
+
+ pu.dirty = 0;
+
+ /* If any blocks stored in the buffer were updated, bind the buffer here
+ to avoid state thrashing. */
+ if(buffered_blocks_updated)
+ buffer->bind();
+ }
+
+ for(vector<ProgramBlock>::iterator i=pu.blocks.begin(); i!=pu.blocks.end(); ++i)
+ if(i->block)
+ i->block->apply(i->bind_point);
+}
+
+
+ProgramData::SharedBlock::SharedBlock(unsigned u, UniformBlock *b):
+ used(u),
+ dirty(u),
+ block(b)
+{ }
+
+
+ProgramData::ProgramBlock::ProgramBlock():
+ bind_point(-1),
+ block(0),
+ shared(0)
+{ }
+
+ProgramData::ProgramBlock::ProgramBlock(int p, SharedBlock *b):
+ bind_point(p),
+ block(b ? b->block : 0),
+ shared(b)
+{ }
+
+
+ProgramData::ProgramUniforms::ProgramUniforms():
+ used(ALL_ONES),
+ dirty(ALL_ONES)
+{ }
+
+
+ProgramData::Loader::Loader(ProgramData &pd):
+ DataFile::ObjectLoader<ProgramData>(pd)
+{
+ add("uniform1i", &Loader::uniform1i);
+ add("uniform1f", &Loader::uniform1f);
+ add("uniform2f", &Loader::uniform2f);
+ add("uniform3f", &Loader::uniform3f);
+ add("uniform4f", &Loader::uniform4f);
+}
+
+void ProgramData::Loader::uniform1i(const string &n, int v)
+{
+ obj.uniform(n, v);
+}
+
+void ProgramData::Loader::uniform1f(const string &n, float v)
+{
+ obj.uniform(n, v);
+}
+
+void ProgramData::Loader::uniform2f(const string &n, float v0, float v1)
+{
+ obj.uniform(n, v0, v1);
+}
+
+void ProgramData::Loader::uniform3f(const string &n, float v0, float v1, float v2)
+{
+ obj.uniform(n, v0, v1, v2);
+}
+
+void ProgramData::Loader::uniform4f(const string &n, float v0, float v1, float v2, float v3)
+{
+ obj.uniform(n, v0, v1, v2, v3);