X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=blobdiff_plain;f=source%2Fprogramdata.cpp;h=cd12c2ca47fbff99cdbfe2578d30ebbd0468d997;hp=7334596c0d4873cfb550b995e5791d8b5236cd2d;hb=56beca9d8b4f7b4edac81411d31e24df88e84ac3;hpb=904de4f7fd994886adbd3a6c03bc1b7c14ebc562 diff --git a/source/programdata.cpp b/source/programdata.cpp index 7334596c..cd12c2ca 100644 --- a/source/programdata.cpp +++ b/source/programdata.cpp @@ -1,7 +1,8 @@ +#include +#include #include "buffer.h" #include "color.h" #include "error.h" -#include "extension.h" #include "matrix.h" #include "program.h" #include "programdata.h" @@ -14,52 +15,101 @@ using namespace std; namespace Msp { namespace GL { -ProgramData::ProgramData(): +ProgramData::ProgramData(const Program *p): + tied_program(p), last_block(0), buffer(0), - changes(NO_CHANGES) -{ - static RequireExtension _ext("GL_ARB_shader_objects"); -} + dirty(0) +{ } // 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), - changes(NO_CHANGES) + dirty(0) { - for(UniformMap::iterator i=uniforms.begin(); i!=uniforms.end(); ++i) - i->second = i->second->clone(); + for(vector::iterator i=uniforms.begin(); i!=uniforms.end(); ++i) + *i = (*i)->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()); + + for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i) + delete i->second.block; + programs.clear(); + + last_block = 0; + buffer = 0; + dirty = 0; + + return *this; } ProgramData::~ProgramData() { - for(UniformMap::iterator i=uniforms.begin(); i!=uniforms.end(); ++i) - delete i->second; + for(vector::iterator i=uniforms.begin(); i!=uniforms.end(); ++i) + delete *i; for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i) delete i->second.block; + delete buffer; } void ProgramData::uniform(const string &name, Uniform *uni) { - UniformMap::iterator i = uniforms.find(name); - if(i!=uniforms.end()) + try + { + if(tied_program) + tied_program->get_uniform_info(name); + else if(name[name.size()-1]==']') + throw invalid_argument("ProgramData::uniform"); + } + catch(...) + { + delete uni; + throw; + } + + SlotMap::iterator i = uniform_slots.find(name); + if(i!=uniform_slots.end()) { - /* UniformBlock does not copy the uniforms, so existing 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 i->second; - i->second = uni; - changes = VALUES_CHANGED; + 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; + else // Force a full update if the mask isn't wide enough + dirty = ALL_ONES; } else { - uniforms[name] = uni; - changes = KEYS_CHANGED; + uniform_slots[name] = uniforms.size(); + uniforms.push_back(uni); + dirty = ALL_ONES; } } +void ProgramData::uniform(const string &name, const Uniform &u) +{ + uniform(name, u.clone()); +} + void ProgramData::uniform(const string &name, int v) { uniform(name, new Uniform1i(v)); @@ -70,17 +120,34 @@ void ProgramData::uniform(const string &name, float v) uniform(name, new Uniform1f(v)); } +void ProgramData::uniform(const string &name, int v0, int v1) +{ + int va[2] = { v0, v1 }; + uniform2(name, va); +} + void ProgramData::uniform(const string &name, float v0, float v1) { float va[2] = { v0, v1 }; uniform2(name, va); } +void ProgramData::uniform2(const string &name, const int *v) +{ + uniform(name, new Uniform2i(v)); +} + void ProgramData::uniform2(const string &name, const float *v) { uniform(name, new Uniform2f(v)); } +void ProgramData::uniform(const string &name, int v0, int v1, int v2) +{ + int va[3] = { v0, v1, v2 }; + uniform3(name, va); +} + void ProgramData::uniform(const string &name, float v0, float v1, float v2) { float va[3] = { v0, v1, v2 }; @@ -92,11 +159,22 @@ void ProgramData::uniform(const string &name, const Vector3 &v) uniform(name, v.x, v.y, v.z); } +void ProgramData::uniform3(const string &name, const int *v) +{ + uniform(name, new Uniform3i(v)); +} + void ProgramData::uniform3(const string &name, const float *v) { uniform(name, new Uniform3f(v)); } +void ProgramData::uniform(const string &name, int v0, int v1, int v2, int v3) +{ + int va[4] = { v0, v1, v2, v3 }; + uniform4(name, va); +} + void ProgramData::uniform(const string &name, float v0, float v1, float v2, float v3) { float va[4] = { v0, v1, v2, v3 }; @@ -113,21 +191,49 @@ void ProgramData::uniform(const string &name, const Color &c) uniform(name, c.r, c.g, c.b, c.a); } +void ProgramData::uniform4(const string &name, const int *v) +{ + uniform(name, new Uniform4i(v)); +} + void ProgramData::uniform4(const string &name, const float *v) { uniform(name, new Uniform4f(v)); } +void ProgramData::uniform(const string &name, const LinAl::Matrix &m) +{ + uniform_matrix2(name, &m(0, 0)); +} + +void ProgramData::uniform_matrix2(const string &name, const float *v) +{ + uniform(name, new UniformMatrix2x2f(v)); +} + +void ProgramData::uniform(const string &name, const LinAl::Matrix &m) +{ + uniform_matrix3(name, &m(0, 0)); +} + +void ProgramData::uniform_matrix3(const string &name, const float *v) +{ + uniform(name, new UniformMatrix3x3f(v)); +} + +void ProgramData::uniform(const string &name, const Matrix &m) +{ + uniform_matrix4(name, m.data()); +} + void ProgramData::uniform_matrix4(const string &name, const float *v) { uniform(name, new UniformMatrix4x4f(v)); } -void ProgramData::uniform_matrix4(const string &name, const Matrix &m) +void ProgramData::uniform1_array(const string &name, unsigned n, const int *v) { - float v[16]; - copy(m.data(), m.data()+16, v); - uniform_matrix4(name, v); + uniform(name, new UniformArray(n, v)); } void ProgramData::uniform1_array(const string &name, unsigned n, const float *v) @@ -135,177 +241,266 @@ void ProgramData::uniform1_array(const string &name, unsigned n, const float *v) uniform(name, new UniformArray(n, v)); } +void ProgramData::uniform2_array(const string &name, unsigned n, const int *v) +{ + uniform(name, new UniformArray(n, v)); +} + void ProgramData::uniform2_array(const string &name, unsigned n, const float *v) { uniform(name, new UniformArray(n, v)); } +void ProgramData::uniform3_array(const string &name, unsigned n, const int *v) +{ + uniform(name, new UniformArray(n, v)); +} + void ProgramData::uniform3_array(const string &name, unsigned n, const float *v) { uniform(name, new UniformArray(n, v)); } +void ProgramData::uniform4_array(const string &name, unsigned n, const int *v) +{ + uniform(name, new UniformArray(n, v)); +} + void ProgramData::uniform4_array(const string &name, unsigned n, const float *v) { uniform(name, new UniformArray(n, v)); } +void ProgramData::uniform_matrix2_array(const string &name, unsigned n, const float *v) +{ + uniform(name, new UniformArray(n, v)); +} + +void ProgramData::uniform_matrix3_array(const string &name, unsigned n, const float *v) +{ + uniform(name, new UniformArray(n, v)); +} + void ProgramData::uniform_matrix4_array(const string &name, unsigned n, const float *v) { uniform(name, new UniformArray(n, v)); } -void ProgramData::find_uniforms_for_block(Block &block, const Program::UniformBlockInfo &info) const +void ProgramData::remove_uniform(const string &name) { - block.uniforms.clear(); - for(vector::const_iterator i=info.uniforms.begin(); i!=info.uniforms.end(); ++i) + SlotMap::iterator i = uniform_slots.find(name); + if(i!=uniform_slots.end()) { - // XXX individual array elements - UniformMap::const_iterator j = uniforms.find((*i)->name); - if(j!=uniforms.end()) - block.uniforms[(*i)->location] = &j->second; + 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; + + uniform_slots.erase(i); + + dirty = ALL_ONES; } } -UniformBlock *ProgramData::create_block(const Program::UniformBlockInfo &info) const +vector ProgramData::get_uniform_names() const { - UniformBlock *block = new UniformBlock(info.data_size); - if(!buffer) - buffer = new Buffer(UNIFORM_BUFFER); - block->use_buffer(buffer, last_block); - last_block = block; - return block; + vector names; + for(SlotMap::const_iterator i=uniform_slots.begin(); i!=uniform_slots.end(); ++i) + names.push_back(i->first); + return names; } -const UniformBlock *ProgramData::get_block(const Program &prog, const Program::UniformBlockInfo *info) const +const Uniform &ProgramData::get_uniform(const string &name) const { - if(changes) + return *uniforms[get_item(uniform_slots, name)]; +} + +unsigned ProgramData::compute_slot_mask(const Program::UniformBlockInfo &block) const +{ + unsigned mask = 0; + for(vector::const_iterator i=block.uniforms.begin(); i!=block.uniforms.end(); ++i) { - for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i) - if(i->second.changessecond.changes = changes; - changes = NO_CHANGES; + 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; } - unsigned layout_hash = (info ? info->layout_hash : prog.get_uniform_layout_hash()); + return mask; +} - map::iterator i = blocks.find(layout_hash); +void ProgramData::update_block(UniformBlock &block, const Program::UniformBlockInfo &info) const +{ + for(vector::const_iterator i=info.uniforms.begin(); i!=info.uniforms.end(); ++i) + { + SlotMap::const_iterator j = uniform_slots.find((*i)->name); + if(j!=uniform_slots.end()) + block.attach(**i, *uniforms[j->second]); + } +} + +ProgramData::SharedBlock *ProgramData::get_shared_block(const Program::UniformBlockInfo &info) const +{ + BlockMap::iterator i = blocks.find(info.layout_hash); if(i==blocks.end()) { - i = blocks.insert(BlockMap::value_type(layout_hash, Block())).first; - if(info) - { - find_uniforms_for_block(i->second, *info); + unsigned used = compute_slot_mask(info); + if(!used) + return 0; - if(!i->second.uniforms.empty()) + UniformBlock *block; + if(info.bind_point>=0) + { + if(!buffer) { - i->second.block = create_block(*info); - i->second.changes = VALUES_CHANGED; + buffer = new Buffer(UNIFORM_BUFFER); + buffer->set_usage(STREAM_DRAW); } + + block = new UniformBlock(info.data_size); + block->use_buffer(buffer, last_block); + last_block = block; } else - { - i->second.block = new UniformBlock; - i->second.changes = VALUES_CHANGED; - } + block = new UniformBlock; + + i = blocks.insert(BlockMap::value_type(info.layout_hash, SharedBlock(used, block))).first; } - else if(info && i->second.changes==KEYS_CHANGED) + + return &i->second; +} + +void ProgramData::apply() const +{ + 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) { - find_uniforms_for_block(i->second, *info); - if(!i->second.uniforms.empty()) + /* 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) { - if(!i->second.block) - i->second.block = create_block(*info); - i->second.changes = VALUES_CHANGED; + 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; } - else - i->second.changes = NO_CHANGES; - } - if(!i->second.block) - return 0; + const Program::UniformBlockMap &prog_blocks = prog->get_uniform_blocks(); - UniformBlock &block = *i->second.block; - if(i->second.changes) - { - if(info) + if(pu.dirty==ALL_ONES) { - vector::const_iterator j = info->uniforms.begin(); - map::const_iterator k = i->second.uniforms.begin(); - while(j!=info->uniforms.end() && k!=i->second.uniforms.end()) + /* 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) { - if(k->first==(*j)->location) + SharedBlock *shared = get_shared_block(i->second); + if(shared) { - block.attach(**j, **k->second); - ++k; + if(shared->dirty==ALL_ONES) + shared->used = compute_slot_mask(i->second); + pu.used |= shared->used; } - ++j; + + pu.blocks.push_back(ProgramBlock(i->second.bind_point, shared)); } } - else + + // Update the contents of all dirty blocks. + bool buffered_blocks_updated = false; + std::vector::iterator j = pu.blocks.begin(); + for(Program::UniformBlockMap::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i, ++j) { - for(UniformMap::const_iterator j=uniforms.begin(); j!=uniforms.end(); ++j) - { - int loc = prog.get_uniform_location(j->first); - if(loc>=0) - block.attach(loc, *j->second); - } + if(!j->shared || !j->shared->dirty) + continue; + + update_block(*j->block, i->second); + j->shared->dirty = 0; + buffered_blocks_updated |= (j->bind_point>=0); } - i->second.changes = NO_CHANGES; - } - return █ -} + pu.dirty = 0; -const UniformBlock *ProgramData::get_block(const Program &prog, const string &name) const -{ - if(name.empty()) - return get_block(prog, 0); - else - return get_block(prog, &prog.get_uniform_block_info(name)); + /* If any blocks stored in the buffer were updated, bind the buffer here + to avoid state thrashing. */ + if(buffered_blocks_updated && !ARB_direct_state_access) + buffer->bind(); + } + + for(vector::iterator i=pu.blocks.begin(); i!=pu.blocks.end(); ++i) + if(i->block) + i->block->apply(i->bind_point); } -void ProgramData::apply() const -{ - const Program *prog = Program::current(); - if(!prog) - throw invalid_operation("ProgramData::apply"); - const Program::UniformBlockMap &prog_blocks = prog->get_uniform_blocks(); - if(!prog_blocks.empty()) - { - typedef pair ApplyBlock; - list apply_blocks; - for(Program::UniformBlockMap::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i) - if(const UniformBlock *block = get_block(*prog, &i->second)) - apply_blocks.push_back(make_pair(block, i->second.bind_point)); +ProgramData::SharedBlock::SharedBlock(unsigned u, UniformBlock *b): + used(u), + dirty(u), + block(b) +{ } - if(buffer) - buffer->bind(); - for(list::const_iterator i=apply_blocks.begin(); i!=apply_blocks.end(); ++i) - i->first->apply(i->second); - } +ProgramData::ProgramBlock::ProgramBlock(): + bind_point(-1), + block(0), + shared(0) +{ } - if(const UniformBlock *block = get_block(*prog, 0)) - block->apply(-1); -} +ProgramData::ProgramBlock::ProgramBlock(int p, SharedBlock *b): + bind_point(p), + block((b && b->used) ? b->block : 0), + shared(b) +{ } -ProgramData::Block::Block(): - changes(NO_CHANGES), - block(0) +ProgramData::ProgramUniforms::ProgramUniforms(): + used(ALL_ONES), + dirty(ALL_ONES) { } ProgramData::Loader::Loader(ProgramData &pd): DataFile::ObjectLoader(pd) { + add("uniform", &Loader::uniform1i); add("uniform1i", &Loader::uniform1i); + add("uniform", &Loader::uniform1f); add("uniform1f", &Loader::uniform1f); + add("uniform", &Loader::uniform2i); + add("uniform2i", &Loader::uniform2i); + add("uniform", &Loader::uniform2f); add("uniform2f", &Loader::uniform2f); + add("uniform", &Loader::uniform3i); + add("uniform3i", &Loader::uniform3i); + add("uniform", &Loader::uniform3f); add("uniform3f", &Loader::uniform3f); + add("uniform", &Loader::uniform4i); + add("uniform4i", &Loader::uniform4i); + add("uniform", &Loader::uniform4f); add("uniform4f", &Loader::uniform4f); + add("uniform1i_array", &Loader::uniform1i_array); + add("uniform1f_array", &Loader::uniform1f_array); + add("uniform2f_array", &Loader::uniform2f_array); + add("uniform3f_array", &Loader::uniform3f_array); + add("uniform4f_array", &Loader::uniform4f_array); + add("uniform_array", &Loader::uniform_array); } void ProgramData::Loader::uniform1i(const string &n, int v) @@ -318,20 +513,200 @@ void ProgramData::Loader::uniform1f(const string &n, float v) obj.uniform(n, v); } +void ProgramData::Loader::uniform2i(const string &n, int v0, int v1) +{ + obj.uniform(n, v0, v1); +} + void ProgramData::Loader::uniform2f(const string &n, float v0, float v1) { obj.uniform(n, v0, v1); } +void ProgramData::Loader::uniform3i(const string &n, int v0, int v1, int v2) +{ + obj.uniform(n, v0, v1, v2); +} + void ProgramData::Loader::uniform3f(const string &n, float v0, float v1, float v2) { obj.uniform(n, v0, v1, v2); } +void ProgramData::Loader::uniform4i(const string &n, int v0, int v1, int v2, int v3) +{ + obj.uniform(n, v0, v1, v2, v3); +} + void ProgramData::Loader::uniform4f(const string &n, float v0, float v1, float v2, float v3) { obj.uniform(n, v0, v1, v2, v3); } +void ProgramData::Loader::uniform_array_(const string &n, DataType t, unsigned e) +{ + ArrayLoader ldr(t, e); + load_sub_with(ldr); + unsigned size = ldr.get_size(); + if(!size) + throw logic_error("empty uniform array"); + + DataType type = ldr.get_data_type(); + unsigned elem_size = ldr.get_element_size(); + if(type==INT) + { + const int *data = reinterpret_cast(ldr.get_data()); + if(elem_size==1) + obj.uniform1_array(n, size, data); + else if(elem_size==2) + obj.uniform2_array(n, size, data); + else if(elem_size==3) + obj.uniform3_array(n, size, data); + else if(elem_size==4) + obj.uniform4_array(n, size, data); + else + throw logic_error("unsupported combination of array type and element size"); + } + else if(type==FLOAT) + { + const float *data = reinterpret_cast(ldr.get_data()); + if(elem_size==1) + obj.uniform1_array(n, size, data); + else if(elem_size==2) + obj.uniform2_array(n, size, data); + else if(elem_size==3) + obj.uniform3_array(n, size, data); + else if(elem_size==4) + obj.uniform4_array(n, size, data); + else + throw logic_error("unsupported combination of array type and element size"); + } + else + throw logic_error("unsupported array type"); +} + +void ProgramData::Loader::uniform1i_array(const string &n) +{ + uniform_array_(n, INT, 1); +} + +void ProgramData::Loader::uniform1f_array(const string &n) +{ + uniform_array_(n, FLOAT, 1); +} + +void ProgramData::Loader::uniform2i_array(const string &n) +{ + uniform_array_(n, INT, 2); +} + +void ProgramData::Loader::uniform2f_array(const string &n) +{ + uniform_array_(n, FLOAT, 2); +} + +void ProgramData::Loader::uniform3i_array(const string &n) +{ + uniform_array_(n, INT, 3); +} + +void ProgramData::Loader::uniform3f_array(const string &n) +{ + uniform_array_(n, FLOAT, 3); +} + +void ProgramData::Loader::uniform4i_array(const string &n) +{ + uniform_array_(n, INT, 4); +} + +void ProgramData::Loader::uniform4f_array(const string &n) +{ + uniform_array_(n, FLOAT, 4); +} + +void ProgramData::Loader::uniform_array(const string &n) +{ + uniform_array_(n, static_cast(0), 0); +} + + +ProgramData::ArrayLoader::ArrayLoader(DataType t, unsigned e): + type(t), + element_size(e) +{ + add("uniform", &ArrayLoader::uniform1i); + add("uniform1i", &ArrayLoader::uniform1i); + add("uniform", &ArrayLoader::uniform1f); + add("uniform1f", &ArrayLoader::uniform1f); + add("uniform", &ArrayLoader::uniform2f); + add("uniform2f", &ArrayLoader::uniform2f); + add("uniform", &ArrayLoader::uniform3f); + add("uniform3f", &ArrayLoader::uniform3f); + add("uniform", &ArrayLoader::uniform4f); + add("uniform4f", &ArrayLoader::uniform4f); +} + +void ProgramData::ArrayLoader::uniform(DataType t, unsigned e, const void *v) +{ + if(element_size && (t!=type || e!=element_size)) + throw logic_error("heterogeneous array contents"); + + if(!element_size) + { + type = t; + element_size = e; + } + + const char *cv = reinterpret_cast(v); + data.insert(data.end(), cv, cv+element_size*4); +} + +void ProgramData::ArrayLoader::uniform1i(int v) +{ + uniform(INT, 1, &v); +} + +void ProgramData::ArrayLoader::uniform1f(float v) +{ + uniform(FLOAT, 1, &v); +} + +void ProgramData::ArrayLoader::uniform2i(int v0, int v1) +{ + int va[2] = { v0, v1 }; + uniform(INT, 2, va); +} + +void ProgramData::ArrayLoader::uniform2f(float v0, float v1) +{ + float va[2] = { v0, v1 }; + uniform(FLOAT, 2, va); +} + +void ProgramData::ArrayLoader::uniform3i(int v0, int v1, int v2) +{ + int va[3] = { v0, v1, v2 }; + uniform(INT, 3, va); +} + +void ProgramData::ArrayLoader::uniform3f(float v0, float v1, float v2) +{ + float va[3] = { v0, v1, v2 }; + uniform(FLOAT, 3, va); +} + +void ProgramData::ArrayLoader::uniform4i(int v0, int v1, int v2, int v3) +{ + int va[4] = { v0, v1, v2, v3 }; + uniform(INT, 4, va); +} + +void ProgramData::ArrayLoader::uniform4f(float v0, float v1, float v2, float v3) +{ + float va[4] = { v0, v1, v2, v3 }; + uniform(FLOAT, 4, va); +} + } // namespace GL } // namespace Msp