From: Mikko Rasa Date: Mon, 25 Jan 2021 00:12:26 +0000 (+0200) Subject: Move buffer resizing out of Bufferable X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=5f78db03b3285c977760a41da1e3927720e50352 Move buffer resizing out of Bufferable Required for immutable buffer storage. This commit has a couple warts I didn't bother to fix because those parts will be rewritten. --- diff --git a/source/buffer.cpp b/source/buffer.cpp index 4b485dda..91aa4028 100644 --- a/source/buffer.cpp +++ b/source/buffer.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "buffer.h" #include "error.h" #include "misc.h" @@ -73,6 +74,12 @@ void Buffer::sub_data(unsigned off, unsigned sz, const void *d) } } +void Buffer::require_size(unsigned req_sz) const +{ + if(size +#include #include #include "gl.h" #include @@ -11,6 +13,13 @@ namespace Msp { namespace GL { +class buffer_too_small: public std::logic_error +{ +public: + buffer_too_small(const std::string &w): std::logic_error(w) { } + virtual ~buffer_too_small() throw() { } +}; + enum BufferType { ARRAY_BUFFER = GL_ARRAY_BUFFER, @@ -87,6 +96,8 @@ public: unsigned get_size() const { return size; } + void require_size(unsigned) const; + BufferRange *create_range(unsigned, unsigned); void *map(BufferAccess); diff --git a/source/bufferable.cpp b/source/bufferable.cpp index e022a716..6124f761 100644 --- a/source/bufferable.cpp +++ b/source/bufferable.cpp @@ -48,6 +48,27 @@ void Bufferable::use_buffer(Buffer *buf, Bufferable *prev) update_offset(); } +void Bufferable::buffer_resized() +{ + for(Bufferable *b=this; b; b=b->next_in_buffer) + { + b->location_dirty = true; + b->dirty = true; + } + for(Bufferable *b=prev_in_buffer; b; b=b->prev_in_buffer) + { + b->location_dirty = true; + b->dirty = true; + } +} + +unsigned Bufferable::get_required_buffer_size() const +{ + const Bufferable *last = this; + for(; last->next_in_buffer; last=last->next_in_buffer) ; + return last->offset+last->get_data_size(); +} + Bufferable::AsyncUpdater *Bufferable::refresh_async() const { return dirty ? new AsyncUpdater(*this) : 0; @@ -92,48 +113,12 @@ void Bufferable::update_offset() } } -bool Bufferable::resize_buffer() const -{ - if(offset+get_data_size()>=buffer->get_size()) - { - const Bufferable *last = this; - for(; last->next_in_buffer; last=last->next_in_buffer) ; - - unsigned total_size = last->offset+last->get_data_size(); - - if(total_size>buffer->get_size()) - { - buffer->data(total_size, 0); - return true; - } - } - - return false; -} - -void Bufferable::update_buffer_size() const -{ - if(resize_buffer()) - { - Conditional _bind(!ARB_direct_state_access, buffer, buffer->get_type()); - - /* Resizing the buffer invalidates its contents. Non-dirty data may - be in use, so reupload it. */ - for(const Bufferable *b=prev_in_buffer; b; b=b->prev_in_buffer) - if(!b->dirty) - b->upload_data(0); - for(const Bufferable *b=next_in_buffer; b; b=b->next_in_buffer) - if(!b->dirty) - b->upload_data(0); - } -} - void Bufferable::upload_data(char *target) const { unsigned data_size = get_data_size(); if(location_dirty) { - update_buffer_size(); + buffer->require_size(offset+data_size); location_changed(buffer, offset, data_size); location_dirty = false; } @@ -152,7 +137,7 @@ void Bufferable::upload_data(char *target) const Bufferable::AsyncUpdater::AsyncUpdater(const Bufferable &b): bufferable(b) { - buffer_resized = bufferable.resize_buffer(); + bufferable.buffer->require_size(bufferable.get_required_buffer_size()); mapped_address = reinterpret_cast(bufferable.buffer->map(WRITE_ONLY)); } @@ -166,10 +151,10 @@ void Bufferable::AsyncUpdater::upload_data() bufferable.upload_data(mapped_address+bufferable.offset); // Update all bufferables in the same buffer at once for(const Bufferable *b=bufferable.prev_in_buffer; b; b=b->prev_in_buffer) - if(b->dirty || buffer_resized) + if(b->dirty) b->upload_data(mapped_address+b->offset); for(const Bufferable *b=bufferable.next_in_buffer; b; b=b->next_in_buffer) - if(b->dirty || buffer_resized) + if(b->dirty) b->upload_data(mapped_address+b->offset); } diff --git a/source/bufferable.h b/source/bufferable.h index 9b2f0bf8..86c5c77f 100644 --- a/source/bufferable.h +++ b/source/bufferable.h @@ -20,7 +20,6 @@ public: private: const Bufferable &bufferable; char *mapped_address; - bool buffer_resized; public: AsyncUpdater(const Bufferable &); @@ -46,6 +45,14 @@ public: buffer, and this object is inserted after it. */ void use_buffer(Buffer *buf, Bufferable *prev = 0); + /** Informs the objects in this chain that the buffer has been resized and + data should be reuploaded. */ + void buffer_resized(); + + /** Returns the total amount of storage required by this object and others + in the same chain, including any alignment between objects. */ + unsigned get_required_buffer_size() const; + /** Uploads new data into the buffer if necessary. */ void refresh() const { if(buffer && dirty) upload_data(0); } @@ -81,10 +88,6 @@ protected: virtual void location_changed(Buffer *, unsigned, unsigned) const { } private: - bool resize_buffer() const; - - void update_buffer_size() const; - /** Uploads data to the buffer. Receives pointer to mapped buffer memory as parameter. If null, buffer interface should be used instead. */ void upload_data(char *) const; diff --git a/source/instancearray.cpp b/source/instancearray.cpp index 91f92084..8fead4e9 100644 --- a/source/instancearray.cpp +++ b/source/instancearray.cpp @@ -75,7 +75,13 @@ void InstanceArray::append(ObjectInstance *inst) if(instance_data) { if(instance_data->size()append(); + unsigned req_size = instance_data->get_required_buffer_size(); + // XXX Inefficient, but will be rewritten imminently + if(instance_buffer->get_size()data(req_size, 0); + } update_instance_matrix(instances.size()-1); } } diff --git a/source/mesh.cpp b/source/mesh.cpp index 8f791121..3569e1c3 100644 --- a/source/mesh.cpp +++ b/source/mesh.cpp @@ -29,6 +29,7 @@ void Mesh::init(ResourceManager *rm) { vbuf = 0; ibuf = 0; + dirty = 0; disallow_rendering = false; winding = 0; @@ -49,20 +50,41 @@ void Mesh::clear() batches.clear(); } -void Mesh::create_buffers() +void Mesh::check_buffers(unsigned mask) { - if(vbuf && ibuf) - return; - - if(!vbuf) - vbuf = new Buffer(ARRAY_BUFFER); - vertices.use_buffer(vbuf); - - if(!ibuf) - ibuf = new Buffer(ELEMENT_ARRAY_BUFFER); + if(mask&VERTEX_BUFFER) + { + if(!vbuf) + { + vbuf = new Buffer(ARRAY_BUFFER); + vertices.use_buffer(vbuf); + vtx_setup.set_vertex_array(vertices); + } + unsigned req_size = vertices.get_required_buffer_size(); + if(vbuf->get_size()get_size()data(vertices.get_required_buffer_size(), 0); + if(dirty&INDEX_BUFFER) + ibuf->data(batches.front().get_required_buffer_size(), 0); + dirty = 0; +} + Resource::AsyncLoader *Mesh::load(IO::Seekable &io, const Resources *) { return new AsyncLoader(*this, io); @@ -197,10 +232,8 @@ void Mesh::Loader::vertices(const vector &c) for(vector::const_iterator i=c.begin(); i!=c.end(); ++i) fmt = (fmt, *i); obj.vertices.reset(fmt); - if(obj.vbuf) - // Set it again to force the vertex setup to update - obj.vtx_setup.set_vertex_array(obj.vertices); load_sub(obj.vertices); + obj.check_buffers(VERTEX_BUFFER); } void Mesh::Loader::batch(PrimitiveType p) @@ -227,7 +260,7 @@ Mesh::AsyncLoader::AsyncLoader(Mesh &m, IO::Seekable &i): phase(0) { mesh.disallow_rendering = true; - mesh.create_buffers(); + mesh.check_buffers(VERTEX_BUFFER|INDEX_BUFFER); } Mesh::AsyncLoader::~AsyncLoader() @@ -253,6 +286,7 @@ bool Mesh::AsyncLoader::process() } else if(phase==1) { + mesh.resize_buffers(); vertex_updater = mesh.vertices.refresh_async(); if(!mesh.batches.empty()) index_updater = mesh.batches.front().refresh_async(); diff --git a/source/mesh.h b/source/mesh.h index 9e086e87..b23510e1 100644 --- a/source/mesh.h +++ b/source/mesh.h @@ -52,13 +52,18 @@ private: virtual bool process(); }; + enum BufferMask + { + VERTEX_BUFFER = 1, + INDEX_BUFFER = 2 + }; + VertexArray vertices; std::vector batches; Buffer *vbuf; Buffer *ibuf; VertexSetup vtx_setup; - bool defer_buffers; - mutable bool dirty; + mutable unsigned short dirty; bool disallow_rendering; const WindingTest *winding; @@ -72,7 +77,7 @@ public: void clear(); private: - void create_buffers(); + void check_buffers(unsigned); public: const VertexArray &get_vertices() const { return vertices; } @@ -90,6 +95,7 @@ public: void draw_instanced(Renderer &, const VertexSetup &, unsigned) const; private: void draw(Renderer &, const VertexSetup *, unsigned) const; + void resize_buffers() const; public: virtual int get_load_priority() const { return 1; } diff --git a/source/meshbuilder.cpp b/source/meshbuilder.cpp index 57e85fc6..9e71536a 100644 --- a/source/meshbuilder.cpp +++ b/source/meshbuilder.cpp @@ -15,6 +15,12 @@ void MeshBuilder::auto_offset() offset(mesh.get_vertices().size()); } +void MeshBuilder::vertex_(const Vector4 &v) +{ + PrimitiveBuilder::vertex_(v); + mesh.check_buffers(Mesh::VERTEX_BUFFER); +} + void MeshBuilder::begin_() { batch = new Batch(type); diff --git a/source/meshbuilder.h b/source/meshbuilder.h index 0755eeb2..afc1450b 100644 --- a/source/meshbuilder.h +++ b/source/meshbuilder.h @@ -19,6 +19,7 @@ public: MeshBuilder(Mesh &); void auto_offset(); private: + virtual void vertex_(const Vector4 &); virtual void begin_(); virtual void end_(); virtual void element_(unsigned); diff --git a/source/programdata.cpp b/source/programdata.cpp index 912bc3c8..68a6715d 100644 --- a/source/programdata.cpp +++ b/source/programdata.cpp @@ -559,6 +559,7 @@ void ProgramData::apply() const const Program::UniformBlockMap &prog_blocks = prog->get_uniform_blocks(); + UniformBlock *old_last_block = last_block; if(pu.dirty==ALL_ONES) { /* The set of uniforms has changed since this program was last used. @@ -600,6 +601,13 @@ void ProgramData::apply() const to avoid state thrashing. */ if(buffered_blocks_updated && !ARB_direct_state_access) buffer->bind(); + + if(last_block!=old_last_block) + { + unsigned required_size = last_block->get_required_buffer_size(); + if(last_block->get_required_buffer_size()>buffer->get_size()) + buffer->data(required_size, 0); + } } for(vector::iterator i=pu.blocks.begin(); i!=pu.blocks.end(); ++i)