]> git.tdb.fi Git - libs/gl.git/commitdiff
Rewrite ProgramData update in a more efficient way
authorMikko Rasa <tdb@tdb.fi>
Wed, 5 Feb 2020 18:07:28 +0000 (20:07 +0200)
committerMikko Rasa <tdb@tdb.fi>
Wed, 5 Feb 2020 18:07:28 +0000 (20:07 +0200)
This method does not do any map lookups on the fast path.

source/programdata.cpp
source/programdata.h

index cd12c2ca47fbff99cdbfe2578d30ebbd0468d997..1e4f5c1d27ceb1e3599079ecd67e455efc387c93 100644 (file)
@@ -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<Uniform *>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-               *i = (*i)->clone();
+       for(vector<NamedUniform>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
+               i->value = i->value->clone();
 }
 
 ProgramData &ProgramData::operator=(const ProgramData &other)
 {
-       for(vector<Uniform *>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-               delete *i;
-       uniforms.clear();
-
        tied_program = other.tied_program;
 
-       uniform_slots = other.uniform_slots;
-       for(vector<Uniform *>::const_iterator i=other.uniforms.begin(); i!=other.uniforms.end(); ++i)
-               uniforms.push_back((*i)->clone());
+       uniforms = other.uniforms;
+       for(vector<NamedUniform>::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<Uniform *>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-               delete *i;
+       for(vector<NamedUniform>::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->second<MASK_BITS)
-                       dirty |= 1<<i->second;
+               uniforms[i].replace_value(uni);
+
+               if(static_cast<unsigned>(i)<MASK_BITS)
+                       dirty |= 1<<i;
                else  // Force a full update if the mask isn't wide enough
                        dirty = ALL_ONES;
+
+               return;
        }
-       else
-       {
-               uniform_slots[name] = uniforms.size();
-               uniforms.push_back(uni);
-               dirty = ALL_ONES;
-       }
+
+       vector<NamedUniform>::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<Uniform *>::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<NamedUniform>::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<string> ProgramData::get_uniform_names() const
 {
        vector<string> 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<NamedUniform>::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 Program::UniformInfo *>::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->second<MASK_BITS)
-                       mask |= 1<<j->second;
-       }
+       return nu.name<name;
+}
 
-       return mask;
+int ProgramData::find_uniform_index(const string &name) const
+{
+       vector<NamedUniform>::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 Program::UniformInfo *>::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; i<info.uniforms.size(); ++i)
+       {
+               int j = find_uniform_index(info.uniforms[i]->name);
+               if(j>=0)
+               {
+                       indices[i] = j;
+                       if(static_cast<unsigned>(j)<MASK_BITS)
+                               block.used |= 1<<j;
+               }
+               else
+                       indices[i] = 0xFF;
+       }
+
+       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
@@ -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 Program::UniformInfo *>::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),
index f11a254cb8014e5ec9a341f5c695c5685b7c802c..08b117b4fbc2712842bfd2967f23201a29b18bcc 100644 (file)
@@ -89,14 +89,36 @@ private:
                ALL_ONES = static_cast<Mask>(-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<std::string, unsigned> SlotMap;
        typedef std::map<Program::LayoutHash, SharedBlock> BlockMap;
        typedef std::map<Program::LayoutHash, ProgramUniforms> 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<Uniform *> uniforms;
+       std::vector<NamedUniform> 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: