From: Mikko Rasa Date: Sun, 19 Dec 2010 10:06:30 +0000 (+0000) Subject: Support different data types in Batch X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=083578c2ca0aabfa60e8872e23d53e9101795dff Support different data types in Batch Select data type automatically based on index values Add GL_NV_primitive_restart extension Automatically concatenate batches of same primitive type if possible Add BufferAlias class for using Buffers with RAII binding --- diff --git a/mesh2c.cpp b/mesh2c.cpp index be72fd4c..c15b829e 100644 --- a/mesh2c.cpp +++ b/mesh2c.cpp @@ -65,12 +65,12 @@ int main(int argc, char **argv) IO::print("%s %sindices[] =\n{", type[0], prefix); for(list::const_iterator i=mesh.get_batches().begin(); i!=mesh.get_batches().end(); ++i) { - const vector &indices = i->get_indices(); - for(unsigned j=0; jsize(); + for(unsigned j=0; jget_index(j)); } } IO::print("\n};\n\n"); @@ -124,8 +124,8 @@ int main(int argc, char **argv) case GL_TRIANGLES: mode = "GL_TRIANGLES"; break; default: mode = format("%d", i->get_type()); break; } - IO::print("\tglDrawElements(%s, %d, %s, %sindices+%d);\n", mode, i->get_indices().size(), type[1], prefix, offset); - offset += i->get_indices().size(); + IO::print("\tglDrawElements(%s, %d, %s, %sindices+%d);\n", mode, i->size(), type[1], prefix, offset); + offset += i->size(); } if(render_func) { diff --git a/source/batch.cpp b/source/batch.cpp index 86ad9435..228ff20b 100644 --- a/source/batch.cpp +++ b/source/batch.cpp @@ -1,12 +1,15 @@ /* $Id$ This file is part of libmspgl -Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Copyright © 2007-2010 Mikko Rasa, Mikkosoft Productions Distributed under the LGPL */ #include "batch.h" +#include "bindable.h" +#include "buffer.h" #include "extension.h" +#include "nv_primitive_restart.h" #include "vertexarray.h" using namespace std; @@ -14,41 +17,285 @@ using namespace std; namespace Msp { namespace GL { +unsigned Batch::restart_index = 0; + Batch::Batch(PrimitiveType t): - type(t), + prim_type(t), + data_type(UNSIGNED_BYTE), min_index(0), - max_index(0) + max_index(0), + restart(false), + ibuf(0), + ibuf_offset(0), + next_in_ibuf(0), + prev_in_ibuf(0), + dirty(false) { } +Batch::~Batch() +{ + unlink_from_ibuf(); +} + +void Batch::set_data_type(DataType t) +{ + if(t!=UNSIGNED_BYTE && t!=UNSIGNED_SHORT && t!=UNSIGNED_INT) + throw InvalidParameterValue("Batch data type must be an unsigned integer"); + if(t==UNSIGNED_BYTE && max_index>0xFE) + throw InvalidState("UNSIGNED_BYTE can't hold all indices in Batch"); + else if(t==UNSIGNED_SHORT && max_index>0xFFFE) + throw InvalidState("UNSIGNED_SHORT can't hold all indices in Batch"); + + if(data_type==UNSIGNED_BYTE && t==UNSIGNED_SHORT) + expand_data(); + else if(data_type==UNSIGNED_BYTE && t==UNSIGNED_INT) + expand_data(); + else if(data_type==UNSIGNED_SHORT && t==UNSIGNED_INT) + expand_data(); + else if(data_type==UNSIGNED_INT && t==UNSIGNED_BYTE) + shrink_data(); + else if(data_type==UNSIGNED_INT && t==UNSIGNED_SHORT) + shrink_data(); + else if(data_type==UNSIGNED_SHORT && t==UNSIGNED_BYTE) + shrink_data(); + + data_type = t; + update_ibuf_offsets(); + dirty = true; +} + +void Batch::use_index_buffer(Buffer *buf, Batch *prev) +{ + if(buf && prev && prev->ibuf!=buf) + throw InvalidParameterValue("Previous batch is not in the same buffer"); + + if(!buf) + { + prev = 0; + unlink_from_ibuf(); + } + + ibuf = buf; + prev_in_ibuf = prev; + next_in_ibuf = 0; + if(prev) + { + prev->next_in_ibuf = this; + ibuf_offset = prev->ibuf_offset+prev->data.size(); + } + else + ibuf_offset = 0; + + dirty = true; +} + Batch &Batch::append(unsigned i) { - if(indices.empty()) + if(data.empty()) min_index = max_index = i; else { min_index = min(min_index, i); max_index = max(max_index, i); } - indices.push_back(i); + + if((data_type==UNSIGNED_BYTE || data_type==UNSIGNED_SHORT) && max_index>0xFFFE) + set_data_type(UNSIGNED_INT); + else if(data_type==UNSIGNED_BYTE && max_index>0xFE) + set_data_type(UNSIGNED_SHORT); + + if(data_type==UNSIGNED_SHORT) + append_index(i); + else if(data_type==UNSIGNED_INT) + append_index(i); + else + data.push_back(i); + + update_ibuf_offsets(); + dirty = true; return *this; } void Batch::append(const vector &ind) { - indices.reserve(indices.size()+ind.size()); + data.reserve(data.size()+ind.size()*get_index_size()); for(vector::const_iterator i=ind.begin(); i!=ind.end(); ++i) append(*i); } +void Batch::append(const Batch &other) +{ + if(other.prim_type!=prim_type) + throw InvalidParameterValue("Can't concatenate batches with different primitive types"); + if(prim_type==LINE_STRIP || prim_type==LINE_LOOP) + throw InvalidState("Can't concatenate line strips or loops"); + else if(prim_type==POLYGON) + throw InvalidState("Can't concatenate polygons"); + else if(prim_type==TRIANGLE_FAN) + static RequireExtension _ext("GL_NV_primitive_restart"); + + if(other.data.empty()) + return; + + if(is_supported("GL_NV_primitive_restart")) + { + restart = true; + if(data_type==UNSIGNED_SHORT) + append_index(0xFFFF); + else if(data_type==UNSIGNED_INT) + append_index(0xFFFFFFFF); + else + data.push_back(0xFF); + } + else if(prim_type==TRIANGLE_STRIP) + { + append(get_index(size()-1)); + append(other.get_index(0)); + if(size()&1) + append(other.get_index(0)); + } + else if(prim_type==QUAD_STRIP) + { + append(get_index(size()-1)); + append(get_index(size()-1)); + append(other.get_index(0)); + append(other.get_index(0)); + } + + unsigned count = other.size(); + for(unsigned i=0; iprev_in_ibuf; b=b->prev_in_ibuf) ; + + unsigned chain_size = 0; + for(const Batch *a=b; a; a=a->next_in_ibuf) + chain_size += a->data.size(); + + ibuf->data(chain_size, 0); + + for(; b; b=b->next_in_ibuf) + { + ibuf->sub_data(b->ibuf_offset, b->data.size(), &b->data[0]); + b->dirty = false; + } + } + + BufferAlias alias(*ibuf); + Bind bind_ibuf(alias, true); + + glDrawRangeElements(prim_type, min_index, max_index, size(), data_type, (void *)ibuf_offset); + } + else + glDrawRangeElements(prim_type, min_index, max_index, size(), data_type, &data[0]); +} + +unsigned Batch::get_index_size() const +{ + if(data_type==UNSIGNED_SHORT) + return sizeof(unsigned short); + else if(data_type==UNSIGNED_INT) + return sizeof(unsigned); + return sizeof(unsigned char); +} + +template +void Batch::append_index(T i) +{ + data.insert(data.end(), sizeof(T), 0); + *(T *)(&data[data.size()-sizeof(T)]) = i; +} + +unsigned Batch::get_index(unsigned i) const +{ + if(data_type==UNSIGNED_SHORT) + return *(unsigned short *)&data[i*sizeof(unsigned short)]; + else if(data_type==UNSIGNED_INT) + return *(unsigned *)&data[i*sizeof(unsigned )]; + else + return data[i]; +} + +template +void Batch::expand_data() +{ + unsigned count = data.size()/sizeof(T); + data.resize(count*sizeof(U)); + for(unsigned i=count; i--;) + *(U *)(&data[i*sizeof(U)]) = convert(*(T *)(&data[i*sizeof(T)])); +} + +template +void Batch::shrink_data() +{ + unsigned count = data.size()/sizeof(T); + for(unsigned i=0; i(*(T *)(&data[i*sizeof(T)])); + data.resize(count*sizeof(U)); +} + +template +U Batch::convert(T i) const +{ + if(!static_cast(~i)) + return ~0; + else + return i; +} + +void Batch::unlink_from_ibuf() +{ + if(next_in_ibuf) + next_in_ibuf->prev_in_ibuf = prev_in_ibuf; + if(prev_in_ibuf) + { + prev_in_ibuf->next_in_ibuf = next_in_ibuf; + prev_in_ibuf->update_ibuf_offsets(); + } + else if(next_in_ibuf) + { + next_in_ibuf->ibuf_offset = 0; + next_in_ibuf->update_ibuf_offsets(); + } } -void Batch::draw_with_buffer(unsigned offset) const +void Batch::update_ibuf_offsets() { - draw_range_elements(type, min_index, max_index, indices.size(), (unsigned *)0+offset); + for(Batch *b=this; b->next_in_ibuf; b=b->next_in_ibuf) + b->next_in_ibuf->ibuf_offset = b->ibuf_offset+b->data.size(); } diff --git a/source/batch.h b/source/batch.h index 2b907d48..c3e9848e 100644 --- a/source/batch.h +++ b/source/batch.h @@ -10,11 +10,22 @@ Distributed under the LGPL #include #include +#include "datatype.h" #include "primitivetype.h" namespace Msp { namespace GL { +class Buffer; + +/** +Stores primitive type and element indices for a single GL draw call. Data +type for indices is automatically chosen to accommodate the largest index in +the Batch. + +This is a pretty low-level class and mainly intended to be used by the Mesh +class. +*/ class Batch { public: @@ -27,21 +38,51 @@ public: }; private: - PrimitiveType type; - std::vector indices; + PrimitiveType prim_type; + DataType data_type; + std::vector data; unsigned min_index; unsigned max_index; + bool restart; + Buffer *ibuf; + unsigned ibuf_offset; + Batch *next_in_ibuf; + Batch *prev_in_ibuf; + mutable bool dirty; + + static unsigned restart_index; public: Batch(PrimitiveType t); + ~Batch(); + PrimitiveType get_type() const { return prim_type; } + void set_data_type(DataType); + DataType get_data_type() const { return data_type; } + void use_index_buffer(Buffer *, Batch * = 0); Batch &append(unsigned); void append(const std::vector &); - PrimitiveType get_type() const { return type; } - unsigned size() const { return indices.size(); } - const std::vector &get_indices() const { return indices; } + void append(const Batch &); + unsigned size() const { return data.size()/get_index_size(); } + unsigned get_index(unsigned) const; void draw() const; - void draw_with_buffer(unsigned) const; +private: + unsigned get_index_size() const; + + template + void append_index(T); + + template + void expand_data(); + + template + void shrink_data(); + + template + U convert(T) const; +private: + void unlink_from_ibuf(); + void update_ibuf_offsets(); }; } // namespace GL diff --git a/source/buffer.h b/source/buffer.h index 7aea9665..1d073ab7 100644 --- a/source/buffer.h +++ b/source/buffer.h @@ -1,7 +1,7 @@ /* $Id$ This file is part of libmspgl -Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Copyright © 2007, 2009-2010 Mikko Rasa, Mikkosoft Productions Distributed under the LGPL */ @@ -84,6 +84,23 @@ private: static void restore(const Buffer *, BufferType); }; +/** +An adaptor for Buffer to make it compatible with Bind. +*/ +template +class BufferAlias +{ +private: + const Buffer &buffer; + +public: + BufferAlias(const Buffer &b): buffer(b) { } + + void bind() const { buffer.bind_to(T); } + static const Buffer *current() { return Buffer::current(T); } + static void unbind() { Buffer::unbind_from(T); } +}; + } // namespace GL } // namespace Msp diff --git a/source/extension.cpp b/source/extension.cpp index bab160d5..c2f1f600 100644 --- a/source/extension.cpp +++ b/source/extension.cpp @@ -20,6 +20,7 @@ Distributed under the LGPL #include "except.h" #include "extension.h" #include "gl.h" +#include "nv_primitive_restart.h" #include "version_1_2.h" #include "version_1_3.h" @@ -53,6 +54,8 @@ bool is_supported(const string &ext) init_ext_framebuffer_object(); if(extensions.count("GL_ARB_vertex_buffer_object")) init_arb_vertex_buffer_object(); + if(extensions.count("GL_NV_primitive_restart")) + init_nv_primitive_restart(); init_done = true; } diff --git a/source/mesh.cpp b/source/mesh.cpp index d421769a..a1930d75 100644 --- a/source/mesh.cpp +++ b/source/mesh.cpp @@ -1,11 +1,12 @@ /* $Id$ This file is part of libmspgl -Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Copyright © 2007-2010 Mikko Rasa, Mikkosoft Productions Distributed under the LGPL */ #include "buffer.h" +#include "extension.h" #include "mesh.h" using namespace std; @@ -15,86 +16,95 @@ namespace GL { Mesh::Mesh(): vertices(VERTEX3), - ibuf(0) + ibuf(0), + defer_ibuf(true) { } Mesh::Mesh(const VertexFormat &f): vertices(f), - ibuf(0) + ibuf(0), + defer_ibuf(true) { } -void Mesh::use_vertex_buffer(bool b) +Mesh::~Mesh() +{ + delete ibuf; +} + +void Mesh::clear() +{ + vertices.clear(); + batches.clear(); +} + +void Mesh::use_buffers(bool b) { if(b) { vertices.use_vertex_buffer(); if(!ibuf) ibuf = new Buffer(ELEMENT_ARRAY_BUFFER); - update_index_buffer(); + defer_ibuf = false; } else { vertices.use_vertex_buffer(0); delete ibuf; ibuf = 0; + defer_ibuf = false; } } -float *Mesh::get_vertex(unsigned i) +unsigned Mesh::get_n_vertices() const { - return vertices[i]; + return vertices.size(); } -void Mesh::add_batch(const Batch &b) +float *Mesh::modify_vertex(unsigned i) { - batches.push_back(b); - update_index_buffer(); + return vertices.modify(i); } -void Mesh::clear() +void Mesh::add_batch(const Batch &b) { - vertices.clear(); - batches.clear(); -} + bool can_append = false; + if(!batches.empty()) + { + PrimitiveType type = b.get_type(); + can_append = (type==batches.back().get_type() && + type!=LINE_STRIP && type!=LINE_LOOP && type!=POLYGON && + (type!=TRIANGLE_FAN || is_supported("GL_NV_primitive_restart"))); + } -void Mesh::draw() const -{ - vertices.apply(); - if(ibuf) + if(defer_ibuf) { - ibuf->bind(); - unsigned offset = 0; - for(list::const_iterator i=batches.begin(); i!=batches.end(); ++i) - { - i->draw_with_buffer(offset); - offset += i->size(); - } - ibuf->unbind(); + ibuf = new Buffer(ELEMENT_ARRAY_BUFFER); + defer_ibuf = false; } + + if(can_append) + batches.back().append(b); else { - for(list::const_iterator i=batches.begin(); i!=batches.end(); ++i) - i->draw(); + Batch *prev = (batches.empty() ? 0 : &batches.back()); + batches.push_back(b); + if(ibuf) + batches.back().use_index_buffer(ibuf, prev); } } -void Mesh::update_index_buffer() +void Mesh::draw() const { - if(!ibuf) - return; + vertices.apply(); - unsigned total = 0; - for(list::const_iterator i=batches.begin(); i!=batches.end(); ++i) - total += i->size(); + if(ibuf) + ibuf->bind_to(ELEMENT_ARRAY_BUFFER); - ibuf->data(total*sizeof(unsigned), 0); - unsigned offset = 0; for(list::const_iterator i=batches.begin(); i!=batches.end(); ++i) - { - ibuf->sub_data(offset*sizeof(unsigned), i->size()*sizeof(unsigned), &i->get_indices()[0]); - offset += i->size(); - } - ibuf->unbind(); + i->draw(); + + if(ibuf) + Buffer::unbind_from(ELEMENT_ARRAY_BUFFER); } @@ -113,8 +123,9 @@ void Mesh::Loader::vertices(VertexFormat f) void Mesh::Loader::batch(PrimitiveType p) { - obj.batches.push_back(Batch(p)); - load_sub(obj.batches.back()); + Batch btc(p); + load_sub(btc); + obj.add_batch(btc); } } // namespace GL diff --git a/source/mesh.h b/source/mesh.h index 044e7084..9ad924f2 100644 --- a/source/mesh.h +++ b/source/mesh.h @@ -1,7 +1,7 @@ /* $Id$ This file is part of libmspgl -Copyright © 2007 Mikko Rasa, Mikkosoft Productions +Copyright © 2007-2010 Mikko Rasa, Mikkosoft Productions Distributed under the LGPL */ @@ -35,20 +35,24 @@ private: VertexArray vertices; std::list batches; Buffer *ibuf; + bool defer_ibuf; public: Mesh(); Mesh(const VertexFormat &f); + ~Mesh(); + + void clear(); + void use_buffers(bool); - void use_vertex_buffer(bool); const VertexArray &get_vertices() const { return vertices; } - float *get_vertex(unsigned); + unsigned get_n_vertices() const; + float *modify_vertex(unsigned); + void add_batch(const Batch &b); const std::list &get_batches() { return batches; } - void clear(); + void draw() const; -private: - void update_index_buffer(); }; } // namespace GL diff --git a/source/nv_primitive_restart.cpp b/source/nv_primitive_restart.cpp new file mode 100644 index 00000000..9a5139f5 --- /dev/null +++ b/source/nv_primitive_restart.cpp @@ -0,0 +1,17 @@ +#include "extension.h" +#include "nv_primitive_restart.h" + +namespace Msp { +namespace GL { + +PFNGLPRIMITIVERESTARTNVPROC glPrimitiveRestartNV=0; +PFNGLPRIMITIVERESTARTINDEXNVPROC glPrimitiveRestartIndexNV=0; + +void init_nv_primitive_restart() +{ + glPrimitiveRestartNV=reinterpret_cast(get_proc_address("glPrimitiveRestartNV")); + glPrimitiveRestartIndexNV=reinterpret_cast(get_proc_address("glPrimitiveRestartIndexNV")); +} + +} // namespace GL +} // namespace Msp diff --git a/source/nv_primitive_restart.h b/source/nv_primitive_restart.h new file mode 100644 index 00000000..b1ff5e16 --- /dev/null +++ b/source/nv_primitive_restart.h @@ -0,0 +1,18 @@ +#ifndef MSP_GL_NV_PRIMITIVE_RESTART_ +#define MSP_GL_NV_PRIMITIVE_RESTART_ + +#include "gl.h" +#include + +namespace Msp { +namespace GL { + +extern PFNGLPRIMITIVERESTARTNVPROC glPrimitiveRestartNV; +extern PFNGLPRIMITIVERESTARTINDEXNVPROC glPrimitiveRestartIndexNV; + +void init_nv_primitive_restart(); + +} // namespace GL +} // namespace Msp + +#endif