for(vector<TaggedUniform>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
i->value = i->value->clone();
- for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i)
- delete i->second.block;
+ for(vector<SharedBlock>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ delete i->block;
programs.clear();
last_block = 0;
{
for(vector<TaggedUniform>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
delete i->value;
- for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ for(vector<SharedBlock>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
{
- if(i->second.indices.type_flag==0xFE)
- delete[] i->second.indices.dynamic.values;
- delete i->second.block;
+ if(i->indices.type_flag==0xFE)
+ delete[] i->indices.dynamic.values;
+ delete i->block;
}
delete buffer;
}
return ((i!=uniforms.end() && i->tag==tag) ? i-uniforms.begin() : -1);
}
+vector<ProgramData::ProgramBlock>::iterator ProgramData::get_program(const Program &prog) const
+{
+ Program::LayoutHash prog_hash = prog.get_uniform_layout_hash();
+ vector<ProgramBlock>::iterator i = lower_bound_member(programs, prog_hash, &ProgramBlock::prog_hash);
+ if(i!=programs.end() && i->prog_hash==prog_hash)
+ return i;
+
+ const vector<Program::UniformBlockInfo> &block_infos = prog.get_uniform_blocks();
+ unsigned index = i-programs.begin();
+ programs.insert(i, 1+block_infos.size(), ProgramBlock(prog_hash));
+
+ /* Block indices may change if new shared blocks need to be inserted. Store
+ the hashes so they can be matched up later. */
+ vector<Program::LayoutHash> block_hashes;
+ block_hashes.reserve(programs.size());
+ for(vector<ProgramBlock>::iterator j=programs.begin(); j!=programs.end(); ++j)
+ block_hashes.push_back(j->block_index>=0 ? blocks[j->block_index].block_hash : 0);
+
+ for(unsigned j=0; j<block_infos.size(); ++j)
+ {
+ const Program::UniformBlockInfo &info = block_infos[j];
+ block_hashes[index+1+j] = info.layout_hash;
+ programs[index+1+j].bind_point = info.bind_point;
+
+ vector<SharedBlock>::iterator k = lower_bound_member(blocks, info.layout_hash, &SharedBlock::block_hash);
+ if(k==blocks.end() || k->block_hash!=info.layout_hash)
+ {
+ k = blocks.insert(k, SharedBlock(info.layout_hash));
+ update_block_uniform_indices(*k, info);
+ }
+ }
+
+ /* Reassign shared block indices from the stored hashes. */
+ for(unsigned j=0; j<programs.size(); ++j)
+ {
+ unsigned hash = block_hashes[j];
+ if(hash)
+ {
+ vector<SharedBlock>::const_iterator k = lower_bound_member(blocks, hash, &SharedBlock::block_hash);
+ programs[j].block_index = k-blocks.begin();
+ }
+ else
+ programs[j].block_index = -1;
+ }
+
+ return programs.begin()+index;
+}
+
void ProgramData::update_block_uniform_indices(SharedBlock &block, const Program::UniformBlockInfo &info) const
{
UInt8 *indices = block.indices.values;
indices = block.indices.dynamic.values;
}
+ bool any_missing = false;
+
block.used = 0;
for(unsigned i=0; i<info.uniforms.size(); ++i)
{
block.used |= 1<<j;
}
else
+ {
indices[i] = 0xFF;
+ any_missing = true;
+ }
}
- block.dirty = block.used;
-}
-
-void ProgramData::update_block(SharedBlock &block, const Program::UniformBlockInfo &info) const
-{
- const UInt8 *indices = block.get_uniform_indices();
- for(unsigned i=0; i<info.uniforms.size(); ++i)
- if(indices[i]!=0xFF)
- block.block->attach(*info.uniforms[i], *uniforms[indices[i]].value);
-}
-
-ProgramData::SharedBlock *ProgramData::get_shared_block(const Program::UniformBlockInfo &info) const
-{
- BlockMap::iterator i = blocks.find(info.layout_hash);
- if(i==blocks.end())
+ if(block.used && any_missing && info.bind_point>=0)
{
- bool any_found = false;
- bool all_found = true;
- for(vector<const Program::UniformInfo *>::const_iterator j=info.uniforms.begin(); j!=info.uniforms.end(); ++j)
- {
- if(find_uniform_index((*j)->name)>=0)
- any_found = true;
- else
- all_found = false;
- }
-
- if(!any_found)
- return 0;
- else if(!all_found && info.bind_point>=0)
- {
#ifdef DEBUG
- IO::print(IO::cerr, "Warning: not all uniforms for block %s are present\n", info.name);
+ IO::print(IO::cerr, "Warning: not all uniforms for block %s are present\n", info.name);
#else
- throw incomplete_uniform_block(info.name);
+ throw incomplete_uniform_block(info.name);
#endif
- }
+ }
+
+ block.dirty = block.used;
- UniformBlock *block;
+ if(block.used && !block.block)
+ {
if(info.bind_point>=0)
{
if(!buffer)
buffer = new Buffer(UNIFORM_BUFFER);
- block = new UniformBlock(info.data_size);
- block->use_buffer(buffer, last_block);
- last_block = block;
+ block.block = new UniformBlock(info.data_size);
+ block.block->use_buffer(buffer, last_block);
+ last_block = block.block;
}
else
- block = new UniformBlock;
-
- i = blocks.insert(BlockMap::value_type(info.layout_hash, SharedBlock(block))).first;
- update_block_uniform_indices(i->second, info);
+ block.block = new UniformBlock;
}
+}
- return &i->second;
+void ProgramData::update_block(SharedBlock &block, const Program::UniformBlockInfo &info) const
+{
+ const UInt8 *indices = block.get_uniform_indices();
+ for(unsigned i=0; i<info.uniforms.size(); ++i)
+ if(indices[i]!=0xFF)
+ block.block->attach(*info.uniforms[i], *uniforms[indices[i]].value);
}
void ProgramData::apply() const
if(!prog)
throw invalid_operation("ProgramData::apply");
- Program::LayoutHash layout = prog->get_uniform_layout_hash();
- ProgramUniforms &pu = programs[layout];
+ UniformBlock *old_last_block = last_block;
+ vector<ProgramBlock>::iterator prog_begin = get_program(*prog);
+ Program::LayoutHash prog_hash = prog->get_uniform_layout_hash();
Mask force_dirty = (dirty==ALL_ONES ? ALL_ONES : 0U);
- Mask affected = (dirty&pu.used) | force_dirty;
- if(affected|pu.dirty)
+ Mask affected = (dirty&prog_begin->masks.used) | force_dirty;
+ if(affected|prog_begin->masks.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
- cause this to happen if there's any dirty uniforms. */
+ /* If the global dirty flag affects this program, add it to per-block and
+ per-program dirty flags and clear the global flag. A previously unseen
+ program will cause this to happen if there's any dirty uniforms. */
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;
+ for(vector<SharedBlock>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ i->dirty |= (dirty&i->used) | force_dirty;
+ for(vector<ProgramBlock>::iterator i=programs.begin(); i!=programs.end(); ++i)
+ if(i->block_index<0)
+ i->masks.dirty |= (dirty&i->masks.used) | force_dirty;
dirty = 0;
}
- const vector<Program::UniformBlockInfo> &prog_blocks = prog->get_uniform_blocks();
+ const vector<Program::UniformBlockInfo> &block_infos = prog->get_uniform_blocks();
- UniformBlock *old_last_block = last_block;
- if(pu.dirty==ALL_ONES)
+ if(prog_begin->masks.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(vector<Program::UniformBlockInfo>::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i)
+ Refresh uniform indices within the program's blocks. */
+ prog_begin->masks.used = 0;
+ vector<ProgramBlock>::iterator j = prog_begin+1;
+ for(vector<Program::UniformBlockInfo>::const_iterator i=block_infos.begin(); i!=block_infos.end(); ++i, ++j)
{
- SharedBlock *shared = get_shared_block(*i);
- if(shared)
- {
- if(shared->dirty==ALL_ONES)
- update_block_uniform_indices(*shared, *i);
- pu.used |= shared->used;
- }
-
- pu.blocks.push_back(ProgramBlock(i->bind_point, shared));
+ SharedBlock &shared = blocks[j->block_index];
+ if(shared.dirty==ALL_ONES)
+ update_block_uniform_indices(shared, *i);
+ prog_begin->masks.used |= shared.used;
+ j->block = (shared.used ? shared.block : 0);
}
}
// Update the contents of all dirty blocks.
bool buffered_blocks_updated = false;
- vector<ProgramBlock>::iterator j = pu.blocks.begin();
- for(vector<Program::UniformBlockInfo>::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i, ++j)
+ vector<ProgramBlock>::iterator j = prog_begin+1;
+ for(vector<Program::UniformBlockInfo>::const_iterator i=block_infos.begin(); i!=block_infos.end(); ++i, ++j)
{
- if(!j->shared || !j->shared->dirty)
- continue;
-
- update_block(*j->shared, *i);
- j->shared->dirty = 0;
- buffered_blocks_updated |= (j->bind_point>=0);
+ SharedBlock &shared = blocks[j->block_index];
+ if(shared.dirty)
+ {
+ update_block(shared, *i);
+ shared.dirty = 0;
+ buffered_blocks_updated |= (j->bind_point>=0);
+ }
}
- pu.dirty = 0;
+ prog_begin->masks.dirty = 0;
/* If any blocks stored in the buffer were updated, bind the buffer here
to avoid state thrashing. */
}
}
- for(vector<ProgramBlock>::iterator i=pu.blocks.begin(); i!=pu.blocks.end(); ++i)
+ for(vector<ProgramBlock>::iterator i=prog_begin+1; (i!=programs.end() && i->prog_hash==prog_hash); ++i)
if(i->block)
i->block->apply(i->bind_point);
}
}
-ProgramData::SharedBlock::SharedBlock(UniformBlock *b):
+ProgramData::SharedBlock::SharedBlock(Program::LayoutHash h):
+ block_hash(h),
used(0),
dirty(0),
- block(b)
+ block(0)
{
indices.type_flag = 0xFD;
}
}
-ProgramData::ProgramBlock::ProgramBlock():
+ProgramData::ProgramBlock::ProgramBlock(Program::LayoutHash h):
+ prog_hash(h),
bind_point(-1),
- block(0),
- shared(0)
-{ }
-
-ProgramData::ProgramBlock::ProgramBlock(int p, SharedBlock *b):
- bind_point(p),
- block((b && b->used) ? b->block : 0),
- shared(b)
-{ }
-
-
-ProgramData::ProgramUniforms::ProgramUniforms():
- used(ALL_ONES),
- dirty(ALL_ONES)
-{ }
+ block_index(-1)
+{
+ masks.used = ALL_ONES;
+ masks.dirty = ALL_ONES;
+}
ProgramData::Loader::Loader(ProgramData &pd):