From 8dd40d3723cb2309ffcca5e041898a767cd7cdea Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 5 Feb 2020 20:07:28 +0200 Subject: [PATCH] Rewrite ProgramData update in a more efficient way This method does not do any map lookups on the fast path. --- source/programdata.cpp | 186 +++++++++++++++++++++++++---------------- source/programdata.h | 38 +++++++-- 2 files changed, 146 insertions(+), 78 deletions(-) diff --git a/source/programdata.cpp b/source/programdata.cpp index cd12c2ca..1e4f5c1d 100644 --- a/source/programdata.cpp +++ b/source/programdata.cpp @@ -25,27 +25,22 @@ ProgramData::ProgramData(const Program *p): // Blocks are intentionally left uncopied ProgramData::ProgramData(const ProgramData &other): tied_program(0), - uniform_slots(other.uniform_slots), uniforms(other.uniforms), last_block(0), buffer(0), dirty(0) { - for(vector::iterator i=uniforms.begin(); i!=uniforms.end(); ++i) - *i = (*i)->clone(); + for(vector::iterator i=uniforms.begin(); i!=uniforms.end(); ++i) + i->value = i->value->clone(); } ProgramData &ProgramData::operator=(const ProgramData &other) { - for(vector::iterator i=uniforms.begin(); i!=uniforms.end(); ++i) - delete *i; - uniforms.clear(); - tied_program = other.tied_program; - uniform_slots = other.uniform_slots; - for(vector::const_iterator i=other.uniforms.begin(); i!=other.uniforms.end(); ++i) - uniforms.push_back((*i)->clone()); + uniforms = other.uniforms; + for(vector::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; @@ -60,10 +55,14 @@ ProgramData &ProgramData::operator=(const ProgramData &other) ProgramData::~ProgramData() { - for(vector::iterator i=uniforms.begin(); i!=uniforms.end(); ++i) - delete *i; + for(vector::iterator i=uniforms.begin(); i!=uniforms.end(); ++i) + delete i->value; for(BlockMap::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; + } delete buffer; } @@ -82,27 +81,27 @@ void ProgramData::uniform(const string &name, Uniform *uni) throw; } - SlotMap::iterator i = uniform_slots.find(name); - if(i!=uniform_slots.end()) + int i = find_uniform_index(name); + if(i>=0) { - Uniform *&slot = uniforms[i->second]; - /* UniformBlock does not copy the uniforms, so existing default blocks - will be left with stale pointers. This is not a problem as long as no - one stores pointers to the blocks and expects them to stay valid. */ - delete slot; - slot = uni; - - if(i->secondsecond; + uniforms[i].replace_value(uni); + + if(static_cast(i)::iterator j = lower_bound(uniforms.begin(), uniforms.end(), name, uniform_name_compare); + + NamedUniform nu; + nu.name = name; + nu.value = uni; + uniforms.insert(j, nu); + + dirty = ALL_ONES; } void ProgramData::uniform(const string &name, const Uniform &u) @@ -288,59 +287,80 @@ void ProgramData::uniform_matrix4_array(const string &name, unsigned n, const fl void ProgramData::remove_uniform(const string &name) { - SlotMap::iterator i = uniform_slots.find(name); - if(i!=uniform_slots.end()) - { - vector::iterator j = uniforms.begin()+i->second; - delete *j; - uniforms.erase(j); - - for(SlotMap::iterator k=uniform_slots.begin(); k!=uniform_slots.end(); ++k) - if(k->second>i->second) - --k->second; + vector::const_iterator i = lower_bound(uniforms.begin(), uniforms.end(), name, uniform_name_compare); + if(i==uniforms.end() || i->name!=name) + return; - uniform_slots.erase(i); + delete i->value; + uniforms.erase(i); - dirty = ALL_ONES; - } + dirty = ALL_ONES; } vector ProgramData::get_uniform_names() const { vector names; - for(SlotMap::const_iterator i=uniform_slots.begin(); i!=uniform_slots.end(); ++i) - names.push_back(i->first); + names.reserve(uniforms.size()); + for(vector::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i) + names.push_back(i->name); return names; } const Uniform &ProgramData::get_uniform(const string &name) const { - return *uniforms[get_item(uniform_slots, name)]; + int i = find_uniform_index(name); + if(i<0) + throw key_error(name); + return *uniforms[i].value; } -unsigned ProgramData::compute_slot_mask(const Program::UniformBlockInfo &block) const +bool ProgramData::uniform_name_compare(const NamedUniform &nu, const string &name) { - unsigned mask = 0; - for(vector::const_iterator i=block.uniforms.begin(); i!=block.uniforms.end(); ++i) - { - SlotMap::const_iterator j = uniform_slots.find((*i)->name); - /* TODO issue a warning (or even error?) either here or in update_block - if all uniforms for a buffer-backed block are not found */ - if(j!=uniform_slots.end() && j->secondsecond; - } + return nu.name::const_iterator i = lower_bound(uniforms.begin(), uniforms.end(), name, uniform_name_compare); + return ((i!=uniforms.end() && i->name==name) ? i-uniforms.begin() : -1); } -void ProgramData::update_block(UniformBlock &block, const Program::UniformBlockInfo &info) const +void ProgramData::update_block_uniform_indices(SharedBlock &block, const Program::UniformBlockInfo &info) const { - for(vector::const_iterator i=info.uniforms.begin(); i!=info.uniforms.end(); ++i) + UInt8 *indices = block.indices.values; + if(info.uniforms.size()>16) { - SlotMap::const_iterator j = uniform_slots.find((*i)->name); - if(j!=uniform_slots.end()) - block.attach(**i, *uniforms[j->second]); + if(block.indices.type_flag==0xFD) + { + block.indices.dynamic.values = new UInt8[info.uniforms.size()]; + block.indices.type_flag = 0xFE; + } + indices = block.indices.dynamic.values; } + + block.used = 0; + for(unsigned i=0; iname); + if(j>=0) + { + indices[i] = j; + if(static_cast(j)attach(*info.uniforms[i], *uniforms[indices[i]].value); } ProgramData::SharedBlock *ProgramData::get_shared_block(const Program::UniformBlockInfo &info) const @@ -348,8 +368,12 @@ ProgramData::SharedBlock *ProgramData::get_shared_block(const Program::UniformBl BlockMap::iterator i = blocks.find(info.layout_hash); if(i==blocks.end()) { - unsigned used = compute_slot_mask(info); - if(!used) + bool any_found = false; + for(vector::const_iterator j=info.uniforms.begin(); (!any_found && j!=info.uniforms.end()); ++j) + any_found = (find_uniform_index((*j)->name)>=0); + + // TODO throw if all uniforms for a buffer-backed block are not found + if(!any_found) return 0; UniformBlock *block; @@ -368,7 +392,8 @@ ProgramData::SharedBlock *ProgramData::get_shared_block(const Program::UniformBl else block = new UniformBlock; - i = blocks.insert(BlockMap::value_type(info.layout_hash, SharedBlock(used, block))).first; + i = blocks.insert(BlockMap::value_type(info.layout_hash, SharedBlock(block))).first; + update_block_uniform_indices(i->second, info); } return &i->second; @@ -415,7 +440,7 @@ void ProgramData::apply() const if(shared) { if(shared->dirty==ALL_ONES) - shared->used = compute_slot_mask(i->second); + update_block_uniform_indices(*shared, i->second); pu.used |= shared->used; } @@ -431,7 +456,7 @@ void ProgramData::apply() const if(!j->shared || !j->shared->dirty) continue; - update_block(*j->block, i->second); + update_block(*j->shared, i->second); j->shared->dirty = 0; buffered_blocks_updated |= (j->bind_point>=0); } @@ -450,12 +475,33 @@ void ProgramData::apply() const } -ProgramData::SharedBlock::SharedBlock(unsigned u, UniformBlock *b): - used(u), - dirty(u), - block(b) +ProgramData::NamedUniform::NamedUniform(): + value(0) { } +void ProgramData::NamedUniform::replace_value(Uniform *v) +{ + /* UniformBlock does not copy the uniforms, so existing default blocks + will be left with stale pointers. This is not a problem as long as no + one stores pointers to the blocks and expects them to stay valid. */ + delete value; + value = v; +} + + +ProgramData::SharedBlock::SharedBlock(UniformBlock *b): + used(0), + dirty(0), + block(b) +{ + indices.type_flag = 0xFD; +} + +const UInt8 *ProgramData::SharedBlock::get_uniform_indices() const +{ + return (indices.type_flag==0xFE ? indices.dynamic.values : indices.values); +} + ProgramData::ProgramBlock::ProgramBlock(): bind_point(-1), diff --git a/source/programdata.h b/source/programdata.h index f11a254c..08b117b4 100644 --- a/source/programdata.h +++ b/source/programdata.h @@ -89,14 +89,36 @@ private: ALL_ONES = static_cast(-1) }; + struct NamedUniform + { + std::string name; + Uniform *value; + + NamedUniform(); + + bool compare_name(const std::string &, unsigned) const; + void replace_value(Uniform *); + }; + struct SharedBlock { Mask used; Mask dirty; UniformBlock *block; - - SharedBlock(); - SharedBlock(unsigned, UniformBlock *); + union + { + UInt8 type_flag; + UInt8 values[16]; + struct + { + UInt8 type_flag; + UInt8 *values; + } dynamic; + } indices; + + SharedBlock(UniformBlock *); + + const UInt8 *get_uniform_indices() const; }; struct ProgramBlock @@ -118,14 +140,12 @@ private: ProgramUniforms(); }; - typedef std::map SlotMap; typedef std::map BlockMap; typedef std::map ProgramMap; // XXX All these mutables are a bit silly, but I'm out of better ideas const Program *tied_program; - SlotMap uniform_slots; - std::vector uniforms; + std::vector uniforms; mutable BlockMap blocks; mutable ProgramMap programs; mutable UniformBlock *last_block; @@ -182,8 +202,10 @@ public: const Uniform &get_uniform(const std::string &) const; private: - unsigned compute_slot_mask(const Program::UniformBlockInfo &) const; - void update_block(UniformBlock &, const Program::UniformBlockInfo &) const; + static bool uniform_name_compare(const NamedUniform &, const std::string &); + int find_uniform_index(const std::string &) const; + void update_block_uniform_indices(SharedBlock &, const Program::UniformBlockInfo &) const; + void update_block(SharedBlock &, const Program::UniformBlockInfo &) const; SharedBlock *get_shared_block(const Program::UniformBlockInfo &) const; public: -- 2.45.2