From ba1427f10d574ff8c4dafc4b8673452b19308ca2 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sat, 10 Apr 2021 21:42:58 +0300 Subject: [PATCH] Refactor ProgramData to store blocks in vectors --- source/render/programdata.cpp | 227 ++++++++++++++++++---------------- source/render/programdata.h | 37 +++--- 2 files changed, 140 insertions(+), 124 deletions(-) diff --git a/source/render/programdata.cpp b/source/render/programdata.cpp index 272489bb..7323df90 100644 --- a/source/render/programdata.cpp +++ b/source/render/programdata.cpp @@ -62,8 +62,8 @@ ProgramData &ProgramData::operator=(const ProgramData &other) 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; + for(vector::iterator i=blocks.begin(); i!=blocks.end(); ++i) + delete i->block; programs.clear(); last_block = 0; @@ -77,11 +77,11 @@ ProgramData::~ProgramData() { for(vector::iterator i=uniforms.begin(); i!=uniforms.end(); ++i) delete i->value; - for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i) + for(vector::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; } @@ -496,6 +496,54 @@ int ProgramData::find_uniform_index(Tag tag) const return ((i!=uniforms.end() && i->tag==tag) ? i-uniforms.begin() : -1); } +vector::iterator ProgramData::get_program(const Program &prog) const +{ + Program::LayoutHash prog_hash = prog.get_uniform_layout_hash(); + vector::iterator i = lower_bound_member(programs, prog_hash, &ProgramBlock::prog_hash); + if(i!=programs.end() && i->prog_hash==prog_hash) + return i; + + const vector &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 block_hashes; + block_hashes.reserve(programs.size()); + for(vector::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::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::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; @@ -509,6 +557,8 @@ void ProgramData::update_block_uniform_indices(SharedBlock &block, const Program indices = block.indices.dynamic.values; } + bool any_missing = false; + block.used = 0; for(unsigned i=0; iattach(*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_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; iattach(*info.uniforms[i], *uniforms[indices[i]].value); } void ProgramData::apply() const @@ -586,64 +617,60 @@ 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::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::iterator i=blocks.begin(); i!=blocks.end(); ++i) + i->dirty |= (dirty&i->used) | force_dirty; + for(vector::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 &prog_blocks = prog->get_uniform_blocks(); + const vector &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::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::iterator j = prog_begin+1; + for(vector::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::iterator j = pu.blocks.begin(); - for(vector::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i, ++j) + vector::iterator j = prog_begin+1; + for(vector::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. */ @@ -667,7 +694,7 @@ void ProgramData::apply() const } } - for(vector::iterator i=pu.blocks.begin(); i!=pu.blocks.end(); ++i) + for(vector::iterator i=prog_begin+1; (i!=programs.end() && i->prog_hash==prog_hash); ++i) if(i->block) i->block->apply(i->bind_point); } @@ -687,10 +714,11 @@ void ProgramData::TaggedUniform::replace_value(Uniform *v) } -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; } @@ -701,23 +729,14 @@ const UInt8 *ProgramData::SharedBlock::get_uniform_indices() const } -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): diff --git a/source/render/programdata.h b/source/render/programdata.h index 97f29343..6f20d4d8 100644 --- a/source/render/programdata.h +++ b/source/render/programdata.h @@ -110,6 +110,7 @@ private: struct SharedBlock { + Program::LayoutHash block_hash; Mask used; Mask dirty; UniformBlock *block; @@ -124,38 +125,34 @@ private: } dynamic; } indices; - SharedBlock(UniformBlock *); + SharedBlock(Program::LayoutHash); const UInt8 *get_uniform_indices() const; }; struct ProgramBlock { + Program::LayoutHash prog_hash; int bind_point; - UniformBlock *block; - SharedBlock *shared; - - ProgramBlock(); - ProgramBlock(int, SharedBlock *); - }; - - struct ProgramUniforms - { - std::vector blocks; - Mask used; - Mask dirty; + int block_index; + union + { + UniformBlock *block; + struct + { + Mask used; + Mask dirty; + } masks; + }; - ProgramUniforms(); + ProgramBlock(Program::LayoutHash); }; - 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; std::vector uniforms; - mutable BlockMap blocks; - mutable ProgramMap programs; + mutable std::vector blocks; + mutable std::vector programs; mutable UniformBlock *last_block; mutable Buffer *buffer; mutable Mask dirty; @@ -237,9 +234,9 @@ public: private: int find_uniform_index(Tag) const; + std::vector::iterator get_program(const Program &) 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: /** Applies uniform blocks for the currently bound program, creating them -- 2.43.0