From: Mikko Rasa Date: Mon, 28 Feb 2022 09:56:33 +0000 (+0200) Subject: Move ProgramData to materials X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=d548a2973f9142648cbf84e3557116b3ee4803c9;p=libs%2Fgl.git Move ProgramData to materials It has more users in materials and in render, and also materials is a lower layer, so it makes more sense for it to be here. --- diff --git a/source/materials/programdata.cpp b/source/materials/programdata.cpp new file mode 100644 index 00000000..e7c17175 --- /dev/null +++ b/source/materials/programdata.cpp @@ -0,0 +1,958 @@ +#include +#include +#include +#include "buffer.h" +#include "color.h" +#include "error.h" +#include "matrix.h" +#include "pipelinestate.h" +#include "program.h" +#include "programdata.h" +#include "uniformblock.h" +#include "vector.h" + +using namespace std; + +namespace Msp { +namespace GL { + +ProgramData::ProgramData(const Program *p): + tied_program(p) +{ } + +ProgramData::ProgramData(ProgramData &&other): + tied_program(other.tied_program), + uniforms(move(other.uniforms)), + uniform_data(move(other.uniform_data)), + generation(other.generation), + blocks(move(other.blocks)), + programs(move(other.programs)), + last_buffer_block(other.last_buffer_block), + buffer(other.buffer), + dirty(other.dirty), + debug_name(move(other.debug_name)) +{ + other.blocks.clear(); + other.buffer = 0; +} + +ProgramData::~ProgramData() +{ + for(SharedBlock &b: blocks) + { + if(b.indices.type_flag==0xFE) + delete[] b.indices.dynamic.values; + delete b.block; + } + delete buffer; +} + +void ProgramData::uniform(Tag tag, DataType type, unsigned array_size, const void *value) +{ + if(!validate_tag(tag)) + return; + + auto i = lower_bound_member(uniforms, tag, &TaggedUniform::tag); + if(i==uniforms.end() || i->tag!=tag) + { + if(uniforms.size()>=MASK_BITS) + throw too_many_uniforms(tag.str()); + + TaggedUniform tu; + tu.tag = tag; + tu.type = type; + tu.array_size = array_size; + tu.data_offset = uniform_data.size(); + tu.data_size = array_size*get_type_size(type); + i = uniforms.insert(i, tu); + uniform_data.resize(tu.data_offset+tu.data_size); + + mark_dirty(ALL_ONES); + } + else if(type!=i->type) + throw invalid_operation("ProgramData::uniform"); + else if(array_size>i->array_size) + { + unsigned add_bytes = (array_size-i->array_size)*get_type_size(type); + uniform_data.insert(uniform_data.begin()+i->data_offset+i->data_size, add_bytes, 0); + for(TaggedUniform &u: uniforms) + if(u.data_offset>i->data_offset) + u.data_offset += add_bytes; + i->array_size = array_size; + i->data_size = array_size*get_type_size(type); + } + + const char *val_begin = static_cast(value); + const char *val_end = val_begin+array_size*get_type_size(type); + char *store_begin = uniform_data.data()+i->data_offset; + copy(val_begin, val_end, store_begin); + + mark_dirty(1<<(i-uniforms.begin())); +} + +bool ProgramData::validate_tag(Tag tag) const +{ +#ifdef DEBUG + try +#endif + { + if(tied_program) + { + const ReflectData::UniformInfo &info = tied_program->get_uniform_info(tag); + if(is_image(info.type)) + throw invalid_operation("ProgramData::uniform"); + } + return true; + } +#ifdef DEBUG + catch(const exception &e) + { + IO::print(IO::cerr, "Error while setting uniform %s: %s: %s\n", tag, Debug::demangle(typeid(e).name()), e.what()); + return false; + } +#endif +} + +void ProgramData::mark_dirty(Mask bits) +{ + if(!dirty) + { + if(generation && !streaming) + { + streaming = true; + if(buffer && buffer->get_size()) + recreate_buffer(); + } + ++generation; + } + dirty |= bits; +} + +void ProgramData::uniform(Tag tag, int v) +{ + uniform(tag, INT, 1, &v); +} + +void ProgramData::uniform(Tag tag, unsigned v) +{ + uniform(tag, UNSIGNED_INT, 1, &v); +} + +void ProgramData::uniform(Tag tag, float v) +{ + uniform(tag, FLOAT, 1, &v); +} + +void ProgramData::uniform(Tag tag, int v0, int v1) +{ + int va[2] = { v0, v1 }; + uniform2(tag, va); +} + +void ProgramData::uniform(Tag tag, unsigned v0, unsigned v1) +{ + unsigned va[2] = { v0, v1 }; + uniform2(tag, va); +} + +void ProgramData::uniform(Tag tag, float v0, float v1) +{ + float va[2] = { v0, v1 }; + uniform2(tag, va); +} + +void ProgramData::uniform2(Tag tag, const int *v) +{ + uniform(tag, INT_VEC2, 1, v); +} + +void ProgramData::uniform2(Tag tag, const unsigned *v) +{ + uniform(tag, UINT_VEC2, 1, v); +} + +void ProgramData::uniform2(Tag tag, const float *v) +{ + uniform(tag, FLOAT_VEC2, 1, v); +} + +void ProgramData::uniform(Tag tag, int v0, int v1, int v2) +{ + int va[3] = { v0, v1, v2 }; + uniform3(tag, va); +} + +void ProgramData::uniform(Tag tag, unsigned v0, unsigned v1, unsigned v2) +{ + unsigned va[3] = { v0, v1, v2 }; + uniform3(tag, va); +} + +void ProgramData::uniform(Tag tag, float v0, float v1, float v2) +{ + float va[3] = { v0, v1, v2 }; + uniform3(tag, va); +} + +void ProgramData::uniform3(Tag tag, const int *v) +{ + uniform(tag, INT_VEC3, 1, v); +} + +void ProgramData::uniform3(Tag tag, const unsigned *v) +{ + uniform(tag, UINT_VEC3, 1, v); +} + +void ProgramData::uniform3(Tag tag, const float *v) +{ + uniform(tag, FLOAT_VEC3, 1, v); +} + +void ProgramData::uniform(Tag tag, int v0, int v1, int v2, int v3) +{ + int va[4] = { v0, v1, v2, v3 }; + uniform4(tag, va); +} + +void ProgramData::uniform(Tag tag, unsigned v0, unsigned v1, unsigned v2, unsigned v3) +{ + unsigned va[4] = { v0, v1, v2, v3 }; + uniform4(tag, va); +} + +void ProgramData::uniform(Tag tag, float v0, float v1, float v2, float v3) +{ + float va[4] = { v0, v1, v2, v3 }; + uniform4(tag, va); +} + +void ProgramData::uniform(Tag tag, const Color &c) +{ + uniform(tag, c.r, c.g, c.b, c.a); +} + +void ProgramData::uniform4(Tag tag, const int *v) +{ + uniform(tag, INT_VEC4, 1, v); +} + +void ProgramData::uniform4(Tag tag, const unsigned *v) +{ + uniform(tag, UINT_VEC4, 1, v); +} + +void ProgramData::uniform4(Tag tag, const float *v) +{ + uniform(tag, FLOAT_VEC4, 1, v); +} + +void ProgramData::uniform_matrix2(Tag tag, const float *v) +{ + uniform(tag, FLOAT_MAT2, 1, v); +} + +void ProgramData::uniform_matrix3x2(Tag tag, const float *v) +{ + uniform(tag, FLOAT_MAT3x2, 1, v); +} + +void ProgramData::uniform_matrix4x2(Tag tag, const float *v) +{ + uniform(tag, FLOAT_MAT4x2, 1, v); +} + +void ProgramData::uniform_matrix2x3(Tag tag, const float *v) +{ + uniform(tag, FLOAT_MAT2x3, 1, v); +} + +void ProgramData::uniform_matrix3(Tag tag, const float *v) +{ + uniform(tag, FLOAT_MAT3, 1, v); +} + +void ProgramData::uniform_matrix4x3(Tag tag, const float *v) +{ + uniform(tag, FLOAT_MAT4x3, 1, v); +} + +void ProgramData::uniform_matrix2x4(Tag tag, const float *v) +{ + uniform(tag, FLOAT_MAT2x4, 1, v); +} + +void ProgramData::uniform_matrix3x4(Tag tag, const float *v) +{ + uniform(tag, FLOAT_MAT3x4, 1, v); +} + +void ProgramData::uniform(Tag tag, const Matrix &m) +{ + uniform_matrix4(tag, m.data()); +} + +void ProgramData::uniform_matrix4(Tag tag, const float *v) +{ + uniform(tag, FLOAT_MAT4, 1, v); +} + +void ProgramData::uniform_array(Tag tag, unsigned n, const int *v) +{ + uniform(tag, INT, n, v); +} + +void ProgramData::uniform_array(Tag tag, unsigned n, const unsigned *v) +{ + uniform(tag, UNSIGNED_INT, n, v); +} + +void ProgramData::uniform_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT, n, v); +} + +void ProgramData::uniform1_array(Tag tag, unsigned n, const int *v) +{ + uniform(tag, INT, n, v); +} + +void ProgramData::uniform1_array(Tag tag, unsigned n, const unsigned *v) +{ + uniform(tag, UNSIGNED_INT, n, v); +} + +void ProgramData::uniform1_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT, n, v); +} + +void ProgramData::uniform2_array(Tag tag, unsigned n, const int *v) +{ + uniform(tag, INT_VEC2, n, v); +} + +void ProgramData::uniform2_array(Tag tag, unsigned n, const unsigned *v) +{ + uniform(tag, UINT_VEC2, n, v); +} + +void ProgramData::uniform2_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT_VEC2, n, v); +} + +void ProgramData::uniform3_array(Tag tag, unsigned n, const int *v) +{ + uniform(tag, INT_VEC3, n, v); +} + +void ProgramData::uniform3_array(Tag tag, unsigned n, const unsigned *v) +{ + uniform(tag, INT_VEC3, n, v); +} + +void ProgramData::uniform3_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT_VEC3, n, v); +} + +void ProgramData::uniform4_array(Tag tag, unsigned n, const int *v) +{ + uniform(tag, INT_VEC4, n, v); +} + +void ProgramData::uniform4_array(Tag tag, unsigned n, const unsigned *v) +{ + uniform(tag, UINT_VEC4, n, v); +} + +void ProgramData::uniform4_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT_VEC4, n, v); +} + +void ProgramData::uniform_matrix2_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT_MAT2, n, v); +} + +void ProgramData::uniform_matrix3x2_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT_MAT3x2, n, v); +} + +void ProgramData::uniform_matrix4x2_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT_MAT4x2, n, v); +} + +void ProgramData::uniform_matrix2x3_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT_MAT2x3, n, v); +} + +void ProgramData::uniform_matrix3_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT_MAT3, n, v); +} + +void ProgramData::uniform_matrix4x3_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT_MAT4x3, n, v); +} + +void ProgramData::uniform_matrix2x4_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT_MAT2x4, n, v); +} + +void ProgramData::uniform_matrix3x4_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT_MAT3x4, n, v); +} + +void ProgramData::uniform_matrix4_array(Tag tag, unsigned n, const float *v) +{ + uniform(tag, FLOAT_MAT4, n, v); +} + +void ProgramData::remove_uniform(Tag tag) +{ + auto i = lower_bound_member(uniforms, tag, &TaggedUniform::tag); + if(i==uniforms.end() || i->tag!=tag) + return; + + uniform_data.erase(uniform_data.begin()+i->data_offset, uniform_data.begin()+i->data_offset+i->data_size); + for(TaggedUniform &u: uniforms) + if(u.data_offset>i->data_offset) + u.data_offset -= i->data_size; + uniforms.erase(i); + + mark_dirty(ALL_ONES); +} + +vector ProgramData::get_uniform_tags() const +{ + vector tags; + tags.reserve(uniforms.size()); + for(const TaggedUniform &u: uniforms) + tags.push_back(u.tag); + return tags; +} + +void ProgramData::copy_uniform(const ProgramData &source, Tag tag) +{ + int i = source.find_uniform_index(tag); + if(i<0) + throw key_error(tag); + const TaggedUniform &tu = source.uniforms[i]; + uniform(tag, tu.type, tu.array_size, source.uniform_data.data()+tu.data_offset); +} + +void ProgramData::copy_uniforms(const ProgramData &source) +{ + for(const TaggedUniform &u: source.uniforms) + uniform(u.tag, u.type, u.array_size, source.uniform_data.data()+u.data_offset); +} + +int ProgramData::find_uniform_index(Tag tag) const +{ + auto i = lower_bound_member(uniforms, tag, &TaggedUniform::tag); + return ((i!=uniforms.end() && i->tag==tag) ? i-uniforms.begin() : -1); +} + +vector::iterator ProgramData::get_program(const Program &prog) const +{ + ReflectData::LayoutHash prog_hash = prog.get_uniform_layout_hash(); + auto 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(const ProgramBlock &b: programs) + block_hashes.push_back(b.block_index>=0 ? blocks[b.block_index].block_hash : 0); + + for(unsigned j=0; jblock_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; jchange_buffer(buffer); + +#ifdef DEBUG + if(!debug_name.empty()) + buffer->set_debug_name(debug_name); +#endif +} + +void ProgramData::update_block_uniform_indices(SharedBlock &block, const ReflectData::UniformBlockInfo &info) const +{ + uint8_t *indices = block.indices.values; + if(info.uniforms.size()>16) + { + if(block.indices.type_flag==0xFD) + { + block.indices.dynamic.values = new uint8_t[info.uniforms.size()]; + block.indices.type_flag = 0xFE; + } + indices = block.indices.dynamic.values; + } + + bool any_missing = false; + + block.used = 0; + for(unsigned i=0; itag); + if(j>=0) + { + indices[i] = j; + if(static_cast(j)=0) + { +#ifdef DEBUG + IO::print(IO::cerr, "Warning: not all uniforms for block %s are present\n", info.name); +#else + throw incomplete_uniform_block(info.name); +#endif + } + + block.dirty = block.used; + + if(block.used && !block.block) + { + block.block = new UniformBlock(info); + if(info.bind_point>=0) + { + if(!buffer) + recreate_buffer(); + + block.block->use_buffer(buffer, last_buffer_block); + last_buffer_block = block.block; + } + } +} + +void ProgramData::update_block(SharedBlock &block, const ReflectData::UniformBlockInfo &info) const +{ + const uint8_t *indices = block.get_uniform_indices(); + for(unsigned i=0; itype)) + ; // Temporarily ignore deprecated use of sampler uniforms in ProgramData + else if(indices[i]!=0xFF) + { + const TaggedUniform &tu = uniforms[indices[i]]; + block.block->store(*info.uniforms[i], tu.array_size, uniform_data.data()+tu.data_offset); + } + } +} + +vector::const_iterator ProgramData::prepare_program(const Program &prog) const +{ + UniformBlock *old_last_block = last_buffer_block; + auto prog_begin = get_program(prog); + + Mask force_dirty = (dirty==ALL_ONES ? ALL_ONES : 0U); + 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-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(SharedBlock &b: blocks) + b.dirty |= (dirty&b.used) | force_dirty; + for(ProgramBlock &b: programs) + if(b.block_index<0) + b.masks.dirty |= (dirty&b.masks.used) | force_dirty; + dirty = 0; + } + + const vector &block_infos = prog.get_uniform_blocks(); + + if(prog_begin->masks.dirty==ALL_ONES) + { + /* The set of uniforms has changed since this program was last used. + Refresh uniform indices within the program's blocks. */ + prog_begin->masks.used = 0; + auto j = prog_begin+1; + for(const ReflectData::UniformBlockInfo &b: block_infos) + { + SharedBlock &shared = blocks[j->block_index]; + if(shared.dirty==ALL_ONES) + update_block_uniform_indices(shared, b); + prog_begin->masks.used |= shared.used; + j->block = (shared.used ? shared.block : 0); + ++j; + } + } + + // Update the contents of all dirty blocks. + bool buffered_blocks_updated = false; + auto j = prog_begin+1; + for(const ReflectData::UniformBlockInfo &b: block_infos) + { + SharedBlock &shared = blocks[j->block_index]; + if(shared.dirty) + { + update_block(shared, b); + shared.dirty = 0; + buffered_blocks_updated |= (j->bind_point>=0); + } + ++j; + } + + prog_begin->masks.dirty = 0; + + if(last_buffer_block!=old_last_block || (buffer && !buffer->get_size())) + { + unsigned required_size = last_buffer_block->get_required_buffer_size(streaming); + if(last_buffer_block->get_required_buffer_size()>buffer->get_size()) + { + if(buffer->get_size()>0) + recreate_buffer(); + + buffer->storage(required_size, (streaming ? STREAMING : STATIC)); + } + } + } + + return prog_begin; +} + +void ProgramData::apply(const Program &prog, PipelineState &state, unsigned frame) const +{ + auto prog_begin = prepare_program(prog); + ReflectData::LayoutHash prog_hash = prog_begin->prog_hash; + + for(auto i=prog_begin+1; (i!=programs.end() && i->prog_hash==prog_hash); ++i) + if(i->block) + { + state.set_uniform_block(i->bind_point, i->block); + if(i->bind_point>=0) + i->block->refresh(frame); + } +} + +void ProgramData::set_debug_name(const string &name) +{ +#ifdef DEBUG + debug_name = name; + if(buffer) + buffer->set_debug_name(name); +#else + (void)name; +#endif +} + + +ProgramData::SharedBlock::SharedBlock(ReflectData::LayoutHash h): + block_hash(h), + used(0), + dirty(0), + block(0) +{ + indices.type_flag = 0xFD; +} + +const uint8_t *ProgramData::SharedBlock::get_uniform_indices() const +{ + return (indices.type_flag==0xFE ? indices.dynamic.values : indices.values); +} + + +ProgramData::ProgramBlock::ProgramBlock(ReflectData::LayoutHash h): + prog_hash(h), + bind_point(-1), + block_index(-1) +{ + masks.used = ALL_ONES; + masks.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) +{ + obj.uniform(n, v); +} + +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 diff --git a/source/materials/programdata.h b/source/materials/programdata.h new file mode 100644 index 00000000..dbbf5f48 --- /dev/null +++ b/source/materials/programdata.h @@ -0,0 +1,287 @@ +#ifndef MSP_GL_PROGRAMDATA_H_ +#define MSP_GL_PROGRAMDATA_H_ + +#include +#include +#include +#include "datatype.h" +#include "matrix.h" +#include "reflectdata.h" +#include "tag.h" +#include "vector.h" + +namespace Msp { +namespace GL { + +class too_many_uniforms: public std::runtime_error +{ +public: + too_many_uniforms(const std::string &w): std::runtime_error(w) { } + virtual ~too_many_uniforms() throw() { } +}; + +class Buffer; +class PipelineState; +class Program; +class UniformBlock; +struct Color; + +/** +Stores uniform values for shader programs. + +The uniforms are stored in a program-independent way, and UniformBlocks are +created to match the uniform layouts of different programs. If multiple +programs have the same layout, the same block is used for them. + +The class is optimized for an access pattern where the set of uniforms and +programs stays constants, with only the values changing. +*/ +class ProgramData: public NonCopyable +{ +public: + class Loader: public DataFile::ObjectLoader + { + public: + Loader(ProgramData &); + private: + void uniform1i(const std::string &, int); + void uniform1f(const std::string &, float); + void uniform2i(const std::string &, int, int); + void uniform2f(const std::string &, float, float); + void uniform3i(const std::string &, int, int, int); + void uniform3f(const std::string &, float, float, float); + void uniform4i(const std::string &, int, int, int, int); + void uniform4f(const std::string &, float, float, float, float); + void uniform_array_(const std::string &, DataType, unsigned); + void uniform1i_array(const std::string &); + void uniform1f_array(const std::string &); + void uniform2i_array(const std::string &); + void uniform2f_array(const std::string &); + void uniform3i_array(const std::string &); + void uniform3f_array(const std::string &); + void uniform4i_array(const std::string &); + void uniform4f_array(const std::string &); + void uniform_array(const std::string &); + }; + +private: + class ArrayLoader: public DataFile::Loader + { + private: + DataType type; + unsigned element_size; + std::vector data; + + public: + ArrayLoader(DataType, unsigned); + + DataType get_data_type() const { return type; } + unsigned get_element_size() const { return element_size; } + const void *get_data() const { return &data[0]; } + unsigned get_size() const { return data.size()/(4*element_size); } + + private: + void uniform(DataType, unsigned, const void *); + void uniform1i(int); + void uniform1f(float); + void uniform2i(int, int); + void uniform2f(float, float); + void uniform3i(int, int, int); + void uniform3f(float, float, float); + void uniform4i(int, int, int, int); + void uniform4f(float, float, float, float); + }; + + typedef unsigned Mask; + + enum + { + MASK_BITS = sizeof(Mask)*8, + ALL_ONES = static_cast(-1) + }; + + struct TaggedUniform + { + Tag tag; + DataType type = VOID; + unsigned array_size = 0; + unsigned data_offset = 0; + unsigned data_size = 0; + }; + + struct SharedBlock + { + ReflectData::LayoutHash block_hash; + Mask used; + Mask dirty; + UniformBlock *block; + union + { + std::uint8_t type_flag; + std::uint8_t values[16]; + struct + { + std::uint8_t type_flag; + std::uint8_t *values; + } dynamic; + } indices; + + SharedBlock(ReflectData::LayoutHash); + + const std::uint8_t *get_uniform_indices() const; + }; + + struct ProgramBlock + { + ReflectData::LayoutHash prog_hash; + int bind_point; + int block_index; + union + { + UniformBlock *block; + struct + { + Mask used; + Mask dirty; + } masks; + }; + + ProgramBlock(ReflectData::LayoutHash); + }; + + // XXX All these mutables are a bit silly, but I'm out of better ideas + const Program *tied_program; + std::vector uniforms; + std::vector uniform_data; + unsigned generation = 0; + mutable std::vector blocks; + mutable std::vector programs; + mutable UniformBlock *last_buffer_block = 0; + mutable Buffer *buffer = 0; + bool streaming = false; + mutable Mask dirty = 0; + std::string debug_name; + +public: + ProgramData(const Program * = 0); + ProgramData(ProgramData &&); + ~ProgramData(); + +private: + void uniform(Tag, DataType, unsigned, const void *); + bool validate_tag(Tag) const; + void mark_dirty(Mask); +public: + void uniform(Tag, int); + void uniform(Tag, unsigned); + void uniform(Tag, float); + void uniform(Tag, int, int); + void uniform(Tag, unsigned, unsigned); + void uniform(Tag, float, float); + void uniform2(Tag, const int *); + void uniform2(Tag, const unsigned *); + void uniform2(Tag, const float *); + void uniform(Tag, int, int, int); + void uniform(Tag, unsigned, unsigned, unsigned); + void uniform(Tag, float, float, float); + void uniform3(Tag, const int *); + void uniform3(Tag, const unsigned *); + void uniform3(Tag, const float *); + void uniform(Tag, int, int, int, int); + void uniform(Tag, unsigned, unsigned, unsigned, unsigned); + void uniform(Tag, float, float, float, float); + void uniform(Tag, const Color &); + void uniform4(Tag, const int *); + void uniform4(Tag, const unsigned *); + void uniform4(Tag, const float *); + void uniform_matrix2(Tag, const float *); + void uniform_matrix3x2(Tag, const float *); + void uniform_matrix4x2(Tag, const float *); + void uniform_matrix2x3(Tag, const float *); + void uniform_matrix3(Tag, const float *); + void uniform_matrix4x3(Tag, const float *); + void uniform_matrix2x4(Tag, const float *); + void uniform_matrix3x4(Tag, const float *); + void uniform(Tag, const Matrix &); + void uniform_matrix4(Tag, const float *); + void uniform_array(Tag, unsigned, const int *); + void uniform_array(Tag, unsigned, const unsigned *); + void uniform_array(Tag, unsigned, const float *); + void uniform1_array(Tag, unsigned, const int *); + void uniform1_array(Tag, unsigned, const unsigned *); + void uniform1_array(Tag, unsigned, const float *); + void uniform2_array(Tag, unsigned, const int *); + void uniform2_array(Tag, unsigned, const unsigned *); + void uniform2_array(Tag, unsigned, const float *); + void uniform3_array(Tag, unsigned, const int *); + void uniform3_array(Tag, unsigned, const unsigned *); + void uniform3_array(Tag, unsigned, const float *); + void uniform4_array(Tag, unsigned, const int *); + void uniform4_array(Tag, unsigned, const unsigned *); + void uniform4_array(Tag, unsigned, const float *); + void uniform_matrix2_array(Tag, unsigned, const float *); + void uniform_matrix3x2_array(Tag, unsigned, const float *); + void uniform_matrix4x2_array(Tag, unsigned, const float *); + void uniform_matrix2x3_array(Tag, unsigned, const float *); + void uniform_matrix3_array(Tag, unsigned, const float *); + void uniform_matrix4x3_array(Tag, unsigned, const float *); + void uniform_matrix2x4_array(Tag, unsigned, const float *); + void uniform_matrix3x4_array(Tag, unsigned, const float *); + void uniform_matrix4_array(Tag, unsigned, const float *); + + template + void uniform(Tag, const LinAl::Vector &); + + template + void uniform(Tag, const LinAl::Matrix &); + + template + void uniform_array(Tag, unsigned, const LinAl::Vector *); + + template + void uniform_array(Tag, unsigned, const LinAl::Matrix *); + + void remove_uniform(Tag); + + unsigned get_generation() const { return generation; } + + std::vector get_uniform_tags() const; + void copy_uniform(const ProgramData &, Tag); + void copy_uniforms(const ProgramData &); + +private: + int find_uniform_index(Tag) const; + std::vector::iterator get_program(const Program &) const; + void recreate_buffer() const; + void update_block_uniform_indices(SharedBlock &, const ReflectData::UniformBlockInfo &) const; + void update_block(SharedBlock &, const ReflectData::UniformBlockInfo &) const; + + std::vector::const_iterator prepare_program(const Program &) const; +public: + /** Creates or updates UniformBlocks for a specific program if necessary, + then sets them to the PipelineState. */ + void apply(const Program &, PipelineState &, unsigned) const; + + void set_debug_name(const std::string &); +}; + +template +void ProgramData::uniform(Tag tag, const LinAl::Vector &v) +{ uniform(tag, TypeTraits>::type, 1, &v.x); } + +template +void ProgramData::uniform(Tag tag, const LinAl::Matrix &v) +{ uniform(tag, TypeTraits>::type, 1, &v(0, 0)); } + +template +void ProgramData::uniform_array(Tag tag, unsigned n, const LinAl::Vector *v) +{ uniform(tag, TypeTraits>::type, n, &v[0].x); } + +template +void ProgramData::uniform_array(Tag tag, unsigned n, const LinAl::Matrix *v) +{ uniform(tag, TypeTraits>::type, n, &v[0](0, 0)); } + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/render/programdata.cpp b/source/render/programdata.cpp deleted file mode 100644 index e7c17175..00000000 --- a/source/render/programdata.cpp +++ /dev/null @@ -1,958 +0,0 @@ -#include -#include -#include -#include "buffer.h" -#include "color.h" -#include "error.h" -#include "matrix.h" -#include "pipelinestate.h" -#include "program.h" -#include "programdata.h" -#include "uniformblock.h" -#include "vector.h" - -using namespace std; - -namespace Msp { -namespace GL { - -ProgramData::ProgramData(const Program *p): - tied_program(p) -{ } - -ProgramData::ProgramData(ProgramData &&other): - tied_program(other.tied_program), - uniforms(move(other.uniforms)), - uniform_data(move(other.uniform_data)), - generation(other.generation), - blocks(move(other.blocks)), - programs(move(other.programs)), - last_buffer_block(other.last_buffer_block), - buffer(other.buffer), - dirty(other.dirty), - debug_name(move(other.debug_name)) -{ - other.blocks.clear(); - other.buffer = 0; -} - -ProgramData::~ProgramData() -{ - for(SharedBlock &b: blocks) - { - if(b.indices.type_flag==0xFE) - delete[] b.indices.dynamic.values; - delete b.block; - } - delete buffer; -} - -void ProgramData::uniform(Tag tag, DataType type, unsigned array_size, const void *value) -{ - if(!validate_tag(tag)) - return; - - auto i = lower_bound_member(uniforms, tag, &TaggedUniform::tag); - if(i==uniforms.end() || i->tag!=tag) - { - if(uniforms.size()>=MASK_BITS) - throw too_many_uniforms(tag.str()); - - TaggedUniform tu; - tu.tag = tag; - tu.type = type; - tu.array_size = array_size; - tu.data_offset = uniform_data.size(); - tu.data_size = array_size*get_type_size(type); - i = uniforms.insert(i, tu); - uniform_data.resize(tu.data_offset+tu.data_size); - - mark_dirty(ALL_ONES); - } - else if(type!=i->type) - throw invalid_operation("ProgramData::uniform"); - else if(array_size>i->array_size) - { - unsigned add_bytes = (array_size-i->array_size)*get_type_size(type); - uniform_data.insert(uniform_data.begin()+i->data_offset+i->data_size, add_bytes, 0); - for(TaggedUniform &u: uniforms) - if(u.data_offset>i->data_offset) - u.data_offset += add_bytes; - i->array_size = array_size; - i->data_size = array_size*get_type_size(type); - } - - const char *val_begin = static_cast(value); - const char *val_end = val_begin+array_size*get_type_size(type); - char *store_begin = uniform_data.data()+i->data_offset; - copy(val_begin, val_end, store_begin); - - mark_dirty(1<<(i-uniforms.begin())); -} - -bool ProgramData::validate_tag(Tag tag) const -{ -#ifdef DEBUG - try -#endif - { - if(tied_program) - { - const ReflectData::UniformInfo &info = tied_program->get_uniform_info(tag); - if(is_image(info.type)) - throw invalid_operation("ProgramData::uniform"); - } - return true; - } -#ifdef DEBUG - catch(const exception &e) - { - IO::print(IO::cerr, "Error while setting uniform %s: %s: %s\n", tag, Debug::demangle(typeid(e).name()), e.what()); - return false; - } -#endif -} - -void ProgramData::mark_dirty(Mask bits) -{ - if(!dirty) - { - if(generation && !streaming) - { - streaming = true; - if(buffer && buffer->get_size()) - recreate_buffer(); - } - ++generation; - } - dirty |= bits; -} - -void ProgramData::uniform(Tag tag, int v) -{ - uniform(tag, INT, 1, &v); -} - -void ProgramData::uniform(Tag tag, unsigned v) -{ - uniform(tag, UNSIGNED_INT, 1, &v); -} - -void ProgramData::uniform(Tag tag, float v) -{ - uniform(tag, FLOAT, 1, &v); -} - -void ProgramData::uniform(Tag tag, int v0, int v1) -{ - int va[2] = { v0, v1 }; - uniform2(tag, va); -} - -void ProgramData::uniform(Tag tag, unsigned v0, unsigned v1) -{ - unsigned va[2] = { v0, v1 }; - uniform2(tag, va); -} - -void ProgramData::uniform(Tag tag, float v0, float v1) -{ - float va[2] = { v0, v1 }; - uniform2(tag, va); -} - -void ProgramData::uniform2(Tag tag, const int *v) -{ - uniform(tag, INT_VEC2, 1, v); -} - -void ProgramData::uniform2(Tag tag, const unsigned *v) -{ - uniform(tag, UINT_VEC2, 1, v); -} - -void ProgramData::uniform2(Tag tag, const float *v) -{ - uniform(tag, FLOAT_VEC2, 1, v); -} - -void ProgramData::uniform(Tag tag, int v0, int v1, int v2) -{ - int va[3] = { v0, v1, v2 }; - uniform3(tag, va); -} - -void ProgramData::uniform(Tag tag, unsigned v0, unsigned v1, unsigned v2) -{ - unsigned va[3] = { v0, v1, v2 }; - uniform3(tag, va); -} - -void ProgramData::uniform(Tag tag, float v0, float v1, float v2) -{ - float va[3] = { v0, v1, v2 }; - uniform3(tag, va); -} - -void ProgramData::uniform3(Tag tag, const int *v) -{ - uniform(tag, INT_VEC3, 1, v); -} - -void ProgramData::uniform3(Tag tag, const unsigned *v) -{ - uniform(tag, UINT_VEC3, 1, v); -} - -void ProgramData::uniform3(Tag tag, const float *v) -{ - uniform(tag, FLOAT_VEC3, 1, v); -} - -void ProgramData::uniform(Tag tag, int v0, int v1, int v2, int v3) -{ - int va[4] = { v0, v1, v2, v3 }; - uniform4(tag, va); -} - -void ProgramData::uniform(Tag tag, unsigned v0, unsigned v1, unsigned v2, unsigned v3) -{ - unsigned va[4] = { v0, v1, v2, v3 }; - uniform4(tag, va); -} - -void ProgramData::uniform(Tag tag, float v0, float v1, float v2, float v3) -{ - float va[4] = { v0, v1, v2, v3 }; - uniform4(tag, va); -} - -void ProgramData::uniform(Tag tag, const Color &c) -{ - uniform(tag, c.r, c.g, c.b, c.a); -} - -void ProgramData::uniform4(Tag tag, const int *v) -{ - uniform(tag, INT_VEC4, 1, v); -} - -void ProgramData::uniform4(Tag tag, const unsigned *v) -{ - uniform(tag, UINT_VEC4, 1, v); -} - -void ProgramData::uniform4(Tag tag, const float *v) -{ - uniform(tag, FLOAT_VEC4, 1, v); -} - -void ProgramData::uniform_matrix2(Tag tag, const float *v) -{ - uniform(tag, FLOAT_MAT2, 1, v); -} - -void ProgramData::uniform_matrix3x2(Tag tag, const float *v) -{ - uniform(tag, FLOAT_MAT3x2, 1, v); -} - -void ProgramData::uniform_matrix4x2(Tag tag, const float *v) -{ - uniform(tag, FLOAT_MAT4x2, 1, v); -} - -void ProgramData::uniform_matrix2x3(Tag tag, const float *v) -{ - uniform(tag, FLOAT_MAT2x3, 1, v); -} - -void ProgramData::uniform_matrix3(Tag tag, const float *v) -{ - uniform(tag, FLOAT_MAT3, 1, v); -} - -void ProgramData::uniform_matrix4x3(Tag tag, const float *v) -{ - uniform(tag, FLOAT_MAT4x3, 1, v); -} - -void ProgramData::uniform_matrix2x4(Tag tag, const float *v) -{ - uniform(tag, FLOAT_MAT2x4, 1, v); -} - -void ProgramData::uniform_matrix3x4(Tag tag, const float *v) -{ - uniform(tag, FLOAT_MAT3x4, 1, v); -} - -void ProgramData::uniform(Tag tag, const Matrix &m) -{ - uniform_matrix4(tag, m.data()); -} - -void ProgramData::uniform_matrix4(Tag tag, const float *v) -{ - uniform(tag, FLOAT_MAT4, 1, v); -} - -void ProgramData::uniform_array(Tag tag, unsigned n, const int *v) -{ - uniform(tag, INT, n, v); -} - -void ProgramData::uniform_array(Tag tag, unsigned n, const unsigned *v) -{ - uniform(tag, UNSIGNED_INT, n, v); -} - -void ProgramData::uniform_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT, n, v); -} - -void ProgramData::uniform1_array(Tag tag, unsigned n, const int *v) -{ - uniform(tag, INT, n, v); -} - -void ProgramData::uniform1_array(Tag tag, unsigned n, const unsigned *v) -{ - uniform(tag, UNSIGNED_INT, n, v); -} - -void ProgramData::uniform1_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT, n, v); -} - -void ProgramData::uniform2_array(Tag tag, unsigned n, const int *v) -{ - uniform(tag, INT_VEC2, n, v); -} - -void ProgramData::uniform2_array(Tag tag, unsigned n, const unsigned *v) -{ - uniform(tag, UINT_VEC2, n, v); -} - -void ProgramData::uniform2_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT_VEC2, n, v); -} - -void ProgramData::uniform3_array(Tag tag, unsigned n, const int *v) -{ - uniform(tag, INT_VEC3, n, v); -} - -void ProgramData::uniform3_array(Tag tag, unsigned n, const unsigned *v) -{ - uniform(tag, INT_VEC3, n, v); -} - -void ProgramData::uniform3_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT_VEC3, n, v); -} - -void ProgramData::uniform4_array(Tag tag, unsigned n, const int *v) -{ - uniform(tag, INT_VEC4, n, v); -} - -void ProgramData::uniform4_array(Tag tag, unsigned n, const unsigned *v) -{ - uniform(tag, UINT_VEC4, n, v); -} - -void ProgramData::uniform4_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT_VEC4, n, v); -} - -void ProgramData::uniform_matrix2_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT_MAT2, n, v); -} - -void ProgramData::uniform_matrix3x2_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT_MAT3x2, n, v); -} - -void ProgramData::uniform_matrix4x2_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT_MAT4x2, n, v); -} - -void ProgramData::uniform_matrix2x3_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT_MAT2x3, n, v); -} - -void ProgramData::uniform_matrix3_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT_MAT3, n, v); -} - -void ProgramData::uniform_matrix4x3_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT_MAT4x3, n, v); -} - -void ProgramData::uniform_matrix2x4_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT_MAT2x4, n, v); -} - -void ProgramData::uniform_matrix3x4_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT_MAT3x4, n, v); -} - -void ProgramData::uniform_matrix4_array(Tag tag, unsigned n, const float *v) -{ - uniform(tag, FLOAT_MAT4, n, v); -} - -void ProgramData::remove_uniform(Tag tag) -{ - auto i = lower_bound_member(uniforms, tag, &TaggedUniform::tag); - if(i==uniforms.end() || i->tag!=tag) - return; - - uniform_data.erase(uniform_data.begin()+i->data_offset, uniform_data.begin()+i->data_offset+i->data_size); - for(TaggedUniform &u: uniforms) - if(u.data_offset>i->data_offset) - u.data_offset -= i->data_size; - uniforms.erase(i); - - mark_dirty(ALL_ONES); -} - -vector ProgramData::get_uniform_tags() const -{ - vector tags; - tags.reserve(uniforms.size()); - for(const TaggedUniform &u: uniforms) - tags.push_back(u.tag); - return tags; -} - -void ProgramData::copy_uniform(const ProgramData &source, Tag tag) -{ - int i = source.find_uniform_index(tag); - if(i<0) - throw key_error(tag); - const TaggedUniform &tu = source.uniforms[i]; - uniform(tag, tu.type, tu.array_size, source.uniform_data.data()+tu.data_offset); -} - -void ProgramData::copy_uniforms(const ProgramData &source) -{ - for(const TaggedUniform &u: source.uniforms) - uniform(u.tag, u.type, u.array_size, source.uniform_data.data()+u.data_offset); -} - -int ProgramData::find_uniform_index(Tag tag) const -{ - auto i = lower_bound_member(uniforms, tag, &TaggedUniform::tag); - return ((i!=uniforms.end() && i->tag==tag) ? i-uniforms.begin() : -1); -} - -vector::iterator ProgramData::get_program(const Program &prog) const -{ - ReflectData::LayoutHash prog_hash = prog.get_uniform_layout_hash(); - auto 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(const ProgramBlock &b: programs) - block_hashes.push_back(b.block_index>=0 ? blocks[b.block_index].block_hash : 0); - - for(unsigned j=0; jblock_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; jchange_buffer(buffer); - -#ifdef DEBUG - if(!debug_name.empty()) - buffer->set_debug_name(debug_name); -#endif -} - -void ProgramData::update_block_uniform_indices(SharedBlock &block, const ReflectData::UniformBlockInfo &info) const -{ - uint8_t *indices = block.indices.values; - if(info.uniforms.size()>16) - { - if(block.indices.type_flag==0xFD) - { - block.indices.dynamic.values = new uint8_t[info.uniforms.size()]; - block.indices.type_flag = 0xFE; - } - indices = block.indices.dynamic.values; - } - - bool any_missing = false; - - block.used = 0; - for(unsigned i=0; itag); - if(j>=0) - { - indices[i] = j; - if(static_cast(j)=0) - { -#ifdef DEBUG - IO::print(IO::cerr, "Warning: not all uniforms for block %s are present\n", info.name); -#else - throw incomplete_uniform_block(info.name); -#endif - } - - block.dirty = block.used; - - if(block.used && !block.block) - { - block.block = new UniformBlock(info); - if(info.bind_point>=0) - { - if(!buffer) - recreate_buffer(); - - block.block->use_buffer(buffer, last_buffer_block); - last_buffer_block = block.block; - } - } -} - -void ProgramData::update_block(SharedBlock &block, const ReflectData::UniformBlockInfo &info) const -{ - const uint8_t *indices = block.get_uniform_indices(); - for(unsigned i=0; itype)) - ; // Temporarily ignore deprecated use of sampler uniforms in ProgramData - else if(indices[i]!=0xFF) - { - const TaggedUniform &tu = uniforms[indices[i]]; - block.block->store(*info.uniforms[i], tu.array_size, uniform_data.data()+tu.data_offset); - } - } -} - -vector::const_iterator ProgramData::prepare_program(const Program &prog) const -{ - UniformBlock *old_last_block = last_buffer_block; - auto prog_begin = get_program(prog); - - Mask force_dirty = (dirty==ALL_ONES ? ALL_ONES : 0U); - 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-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(SharedBlock &b: blocks) - b.dirty |= (dirty&b.used) | force_dirty; - for(ProgramBlock &b: programs) - if(b.block_index<0) - b.masks.dirty |= (dirty&b.masks.used) | force_dirty; - dirty = 0; - } - - const vector &block_infos = prog.get_uniform_blocks(); - - if(prog_begin->masks.dirty==ALL_ONES) - { - /* The set of uniforms has changed since this program was last used. - Refresh uniform indices within the program's blocks. */ - prog_begin->masks.used = 0; - auto j = prog_begin+1; - for(const ReflectData::UniformBlockInfo &b: block_infos) - { - SharedBlock &shared = blocks[j->block_index]; - if(shared.dirty==ALL_ONES) - update_block_uniform_indices(shared, b); - prog_begin->masks.used |= shared.used; - j->block = (shared.used ? shared.block : 0); - ++j; - } - } - - // Update the contents of all dirty blocks. - bool buffered_blocks_updated = false; - auto j = prog_begin+1; - for(const ReflectData::UniformBlockInfo &b: block_infos) - { - SharedBlock &shared = blocks[j->block_index]; - if(shared.dirty) - { - update_block(shared, b); - shared.dirty = 0; - buffered_blocks_updated |= (j->bind_point>=0); - } - ++j; - } - - prog_begin->masks.dirty = 0; - - if(last_buffer_block!=old_last_block || (buffer && !buffer->get_size())) - { - unsigned required_size = last_buffer_block->get_required_buffer_size(streaming); - if(last_buffer_block->get_required_buffer_size()>buffer->get_size()) - { - if(buffer->get_size()>0) - recreate_buffer(); - - buffer->storage(required_size, (streaming ? STREAMING : STATIC)); - } - } - } - - return prog_begin; -} - -void ProgramData::apply(const Program &prog, PipelineState &state, unsigned frame) const -{ - auto prog_begin = prepare_program(prog); - ReflectData::LayoutHash prog_hash = prog_begin->prog_hash; - - for(auto i=prog_begin+1; (i!=programs.end() && i->prog_hash==prog_hash); ++i) - if(i->block) - { - state.set_uniform_block(i->bind_point, i->block); - if(i->bind_point>=0) - i->block->refresh(frame); - } -} - -void ProgramData::set_debug_name(const string &name) -{ -#ifdef DEBUG - debug_name = name; - if(buffer) - buffer->set_debug_name(name); -#else - (void)name; -#endif -} - - -ProgramData::SharedBlock::SharedBlock(ReflectData::LayoutHash h): - block_hash(h), - used(0), - dirty(0), - block(0) -{ - indices.type_flag = 0xFD; -} - -const uint8_t *ProgramData::SharedBlock::get_uniform_indices() const -{ - return (indices.type_flag==0xFE ? indices.dynamic.values : indices.values); -} - - -ProgramData::ProgramBlock::ProgramBlock(ReflectData::LayoutHash h): - prog_hash(h), - bind_point(-1), - block_index(-1) -{ - masks.used = ALL_ONES; - masks.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) -{ - obj.uniform(n, v); -} - -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 diff --git a/source/render/programdata.h b/source/render/programdata.h deleted file mode 100644 index dbbf5f48..00000000 --- a/source/render/programdata.h +++ /dev/null @@ -1,287 +0,0 @@ -#ifndef MSP_GL_PROGRAMDATA_H_ -#define MSP_GL_PROGRAMDATA_H_ - -#include -#include -#include -#include "datatype.h" -#include "matrix.h" -#include "reflectdata.h" -#include "tag.h" -#include "vector.h" - -namespace Msp { -namespace GL { - -class too_many_uniforms: public std::runtime_error -{ -public: - too_many_uniforms(const std::string &w): std::runtime_error(w) { } - virtual ~too_many_uniforms() throw() { } -}; - -class Buffer; -class PipelineState; -class Program; -class UniformBlock; -struct Color; - -/** -Stores uniform values for shader programs. - -The uniforms are stored in a program-independent way, and UniformBlocks are -created to match the uniform layouts of different programs. If multiple -programs have the same layout, the same block is used for them. - -The class is optimized for an access pattern where the set of uniforms and -programs stays constants, with only the values changing. -*/ -class ProgramData: public NonCopyable -{ -public: - class Loader: public DataFile::ObjectLoader - { - public: - Loader(ProgramData &); - private: - void uniform1i(const std::string &, int); - void uniform1f(const std::string &, float); - void uniform2i(const std::string &, int, int); - void uniform2f(const std::string &, float, float); - void uniform3i(const std::string &, int, int, int); - void uniform3f(const std::string &, float, float, float); - void uniform4i(const std::string &, int, int, int, int); - void uniform4f(const std::string &, float, float, float, float); - void uniform_array_(const std::string &, DataType, unsigned); - void uniform1i_array(const std::string &); - void uniform1f_array(const std::string &); - void uniform2i_array(const std::string &); - void uniform2f_array(const std::string &); - void uniform3i_array(const std::string &); - void uniform3f_array(const std::string &); - void uniform4i_array(const std::string &); - void uniform4f_array(const std::string &); - void uniform_array(const std::string &); - }; - -private: - class ArrayLoader: public DataFile::Loader - { - private: - DataType type; - unsigned element_size; - std::vector data; - - public: - ArrayLoader(DataType, unsigned); - - DataType get_data_type() const { return type; } - unsigned get_element_size() const { return element_size; } - const void *get_data() const { return &data[0]; } - unsigned get_size() const { return data.size()/(4*element_size); } - - private: - void uniform(DataType, unsigned, const void *); - void uniform1i(int); - void uniform1f(float); - void uniform2i(int, int); - void uniform2f(float, float); - void uniform3i(int, int, int); - void uniform3f(float, float, float); - void uniform4i(int, int, int, int); - void uniform4f(float, float, float, float); - }; - - typedef unsigned Mask; - - enum - { - MASK_BITS = sizeof(Mask)*8, - ALL_ONES = static_cast(-1) - }; - - struct TaggedUniform - { - Tag tag; - DataType type = VOID; - unsigned array_size = 0; - unsigned data_offset = 0; - unsigned data_size = 0; - }; - - struct SharedBlock - { - ReflectData::LayoutHash block_hash; - Mask used; - Mask dirty; - UniformBlock *block; - union - { - std::uint8_t type_flag; - std::uint8_t values[16]; - struct - { - std::uint8_t type_flag; - std::uint8_t *values; - } dynamic; - } indices; - - SharedBlock(ReflectData::LayoutHash); - - const std::uint8_t *get_uniform_indices() const; - }; - - struct ProgramBlock - { - ReflectData::LayoutHash prog_hash; - int bind_point; - int block_index; - union - { - UniformBlock *block; - struct - { - Mask used; - Mask dirty; - } masks; - }; - - ProgramBlock(ReflectData::LayoutHash); - }; - - // XXX All these mutables are a bit silly, but I'm out of better ideas - const Program *tied_program; - std::vector uniforms; - std::vector uniform_data; - unsigned generation = 0; - mutable std::vector blocks; - mutable std::vector programs; - mutable UniformBlock *last_buffer_block = 0; - mutable Buffer *buffer = 0; - bool streaming = false; - mutable Mask dirty = 0; - std::string debug_name; - -public: - ProgramData(const Program * = 0); - ProgramData(ProgramData &&); - ~ProgramData(); - -private: - void uniform(Tag, DataType, unsigned, const void *); - bool validate_tag(Tag) const; - void mark_dirty(Mask); -public: - void uniform(Tag, int); - void uniform(Tag, unsigned); - void uniform(Tag, float); - void uniform(Tag, int, int); - void uniform(Tag, unsigned, unsigned); - void uniform(Tag, float, float); - void uniform2(Tag, const int *); - void uniform2(Tag, const unsigned *); - void uniform2(Tag, const float *); - void uniform(Tag, int, int, int); - void uniform(Tag, unsigned, unsigned, unsigned); - void uniform(Tag, float, float, float); - void uniform3(Tag, const int *); - void uniform3(Tag, const unsigned *); - void uniform3(Tag, const float *); - void uniform(Tag, int, int, int, int); - void uniform(Tag, unsigned, unsigned, unsigned, unsigned); - void uniform(Tag, float, float, float, float); - void uniform(Tag, const Color &); - void uniform4(Tag, const int *); - void uniform4(Tag, const unsigned *); - void uniform4(Tag, const float *); - void uniform_matrix2(Tag, const float *); - void uniform_matrix3x2(Tag, const float *); - void uniform_matrix4x2(Tag, const float *); - void uniform_matrix2x3(Tag, const float *); - void uniform_matrix3(Tag, const float *); - void uniform_matrix4x3(Tag, const float *); - void uniform_matrix2x4(Tag, const float *); - void uniform_matrix3x4(Tag, const float *); - void uniform(Tag, const Matrix &); - void uniform_matrix4(Tag, const float *); - void uniform_array(Tag, unsigned, const int *); - void uniform_array(Tag, unsigned, const unsigned *); - void uniform_array(Tag, unsigned, const float *); - void uniform1_array(Tag, unsigned, const int *); - void uniform1_array(Tag, unsigned, const unsigned *); - void uniform1_array(Tag, unsigned, const float *); - void uniform2_array(Tag, unsigned, const int *); - void uniform2_array(Tag, unsigned, const unsigned *); - void uniform2_array(Tag, unsigned, const float *); - void uniform3_array(Tag, unsigned, const int *); - void uniform3_array(Tag, unsigned, const unsigned *); - void uniform3_array(Tag, unsigned, const float *); - void uniform4_array(Tag, unsigned, const int *); - void uniform4_array(Tag, unsigned, const unsigned *); - void uniform4_array(Tag, unsigned, const float *); - void uniform_matrix2_array(Tag, unsigned, const float *); - void uniform_matrix3x2_array(Tag, unsigned, const float *); - void uniform_matrix4x2_array(Tag, unsigned, const float *); - void uniform_matrix2x3_array(Tag, unsigned, const float *); - void uniform_matrix3_array(Tag, unsigned, const float *); - void uniform_matrix4x3_array(Tag, unsigned, const float *); - void uniform_matrix2x4_array(Tag, unsigned, const float *); - void uniform_matrix3x4_array(Tag, unsigned, const float *); - void uniform_matrix4_array(Tag, unsigned, const float *); - - template - void uniform(Tag, const LinAl::Vector &); - - template - void uniform(Tag, const LinAl::Matrix &); - - template - void uniform_array(Tag, unsigned, const LinAl::Vector *); - - template - void uniform_array(Tag, unsigned, const LinAl::Matrix *); - - void remove_uniform(Tag); - - unsigned get_generation() const { return generation; } - - std::vector get_uniform_tags() const; - void copy_uniform(const ProgramData &, Tag); - void copy_uniforms(const ProgramData &); - -private: - int find_uniform_index(Tag) const; - std::vector::iterator get_program(const Program &) const; - void recreate_buffer() const; - void update_block_uniform_indices(SharedBlock &, const ReflectData::UniformBlockInfo &) const; - void update_block(SharedBlock &, const ReflectData::UniformBlockInfo &) const; - - std::vector::const_iterator prepare_program(const Program &) const; -public: - /** Creates or updates UniformBlocks for a specific program if necessary, - then sets them to the PipelineState. */ - void apply(const Program &, PipelineState &, unsigned) const; - - void set_debug_name(const std::string &); -}; - -template -void ProgramData::uniform(Tag tag, const LinAl::Vector &v) -{ uniform(tag, TypeTraits>::type, 1, &v.x); } - -template -void ProgramData::uniform(Tag tag, const LinAl::Matrix &v) -{ uniform(tag, TypeTraits>::type, 1, &v(0, 0)); } - -template -void ProgramData::uniform_array(Tag tag, unsigned n, const LinAl::Vector *v) -{ uniform(tag, TypeTraits>::type, n, &v[0].x); } - -template -void ProgramData::uniform_array(Tag tag, unsigned n, const LinAl::Matrix *v) -{ uniform(tag, TypeTraits>::type, n, &v[0](0, 0)); } - -} // namespace GL -} // namespace Msp - -#endif