]> git.tdb.fi Git - libs/gl.git/commitdiff
Move buffer resizing out of Bufferable
authorMikko Rasa <tdb@tdb.fi>
Mon, 25 Jan 2021 00:12:26 +0000 (02:12 +0200)
committerMikko Rasa <tdb@tdb.fi>
Mon, 25 Jan 2021 00:22:36 +0000 (02:22 +0200)
Required for immutable buffer storage.  This commit has a couple warts
I didn't bother to fix because those parts will be rewritten.

source/buffer.cpp
source/buffer.h
source/bufferable.cpp
source/bufferable.h
source/instancearray.cpp
source/mesh.cpp
source/mesh.h
source/meshbuilder.cpp
source/meshbuilder.h
source/programdata.cpp

index 4b485ddad6cb266d1d96d2e844c40583cb9ace3a..91aa4028a1bc3d9279fffddc7912825762a03c36 100644 (file)
@@ -1,6 +1,7 @@
 #include <stdexcept>
 #include <msp/gl/extensions/arb_direct_state_access.h>
 #include <msp/gl/extensions/arb_map_buffer_range.h>
+#include <msp/strings/format.h>
 #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<req_sz)
+               throw buffer_too_small(format("buffer has %d bytes; %d required", size, req_sz));
+}
+
 BufferRange *Buffer::create_range(unsigned s, unsigned o)
 {
        return new BufferRange(*this, s, o);
index 39e88cd1ce7647b74ea5f7f6b75cba575ea0d403..fc9ace78c0ac0f48194a20fdcb1a8e4012be01e1 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef MSP_GL_BUFFER_H_
 #define MSP_GL_BUFFER_H_
 
+#include <stdexcept>
+#include <string>
 #include <vector>
 #include "gl.h"
 #include <msp/gl/extensions/arb_pixel_buffer_object.h>
 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);
index e022a71676eed0a52ee36ed9be087b3eac1e9999..6124f7616660bc62507928f1965c83947adb1ec6 100644 (file)
@@ -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<BindRestore> _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<char *>(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);
 }
 
index 9b2f0bf8bfe2fb58323a3be15f781d8b5aa83091..86c5c77fc4d39226295c3e442903eb476e85de1b 100644 (file)
@@ -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;
index 91f92084d94a680bde115f938b257bdabe8dbd29..8fead4e907fb1e7dff900306ae8dca5367a1f280 100644 (file)
@@ -75,7 +75,13 @@ void InstanceArray::append(ObjectInstance *inst)
        if(instance_data)
        {
                if(instance_data->size()<instances.size())
+               {
                        instance_data->append();
+                       unsigned req_size = instance_data->get_required_buffer_size();
+                       // XXX Inefficient, but will be rewritten imminently
+                       if(instance_buffer->get_size()<req_size)
+                               instance_buffer->data(req_size, 0);
+               }
                update_instance_matrix(instances.size()-1);
        }
 }
index 8f7911219ec68afa3cb9048d35960bef3462437c..3569e1c38badaac3a1f705769bfd4e6e625ff3e0 100644 (file)
@@ -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()<req_size)
+               {
+                       dirty |= VERTEX_BUFFER;
+                       vertices.buffer_resized();
+               }
+       }
 
-       vtx_setup.set_vertex_array(vertices);
-       vtx_setup.set_index_buffer(*ibuf);
+       if(mask&INDEX_BUFFER)
+       {
+               if(!ibuf)
+               {
+                       ibuf = new Buffer(ELEMENT_ARRAY_BUFFER);
+                       if(!batches.empty())
+                               batches.front().use_buffer(ibuf);
+                       vtx_setup.set_index_buffer(*ibuf);
+               }
+               unsigned req_size = (batches.empty() ? 0 : batches.front().get_required_buffer_size());
+               if(ibuf->get_size()<req_size)
+               {
+                       dirty |= INDEX_BUFFER;
+                       if(!batches.empty())
+                               batches.front().buffer_resized();
+               }
+       }
 }
 
 unsigned Mesh::get_n_vertices() const
@@ -77,12 +99,11 @@ float *Mesh::modify_vertex(unsigned i)
 
 void Mesh::add_batch(const Batch &b)
 {
-       create_buffers();
-
        if(batches.empty())
        {
                batches.push_back(b);
-               batches.back().use_buffer(ibuf);
+               if(ibuf)
+                       batches.back().use_buffer(ibuf);
        }
        else if(batches.back().can_append(b.get_type()))
                batches.back().append(b);
@@ -109,6 +130,8 @@ void Mesh::add_batch(const Batch &b)
                else
                        batches.back().use_buffer(ibuf, prev);
        }
+
+       check_buffers(INDEX_BUFFER);
 }
 
 void Mesh::set_winding(const WindingTest *w)
@@ -138,6 +161,9 @@ void Mesh::draw(Renderer &renderer, const VertexSetup *vs, unsigned count) const
                        return;
        }
 
+       if(dirty)
+               resize_buffers();
+
        renderer.set_vertex_setup(vs ? vs : &vtx_setup);
        renderer.set_winding_test(winding);
 
@@ -153,6 +179,15 @@ void Mesh::draw(Renderer &renderer, const VertexSetup *vs, unsigned count) const
        }
 }
 
+void Mesh::resize_buffers() const
+{
+       if(dirty&VERTEX_BUFFER)
+               vbuf->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<VertexComponent> &c)
        for(vector<VertexComponent>::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();
index 9e086e8770502778167702a3bebaa6cc935f6148..b23510e10b14485e8b9c531aee28f8a171d7bea3 100644 (file)
@@ -52,13 +52,18 @@ private:
                virtual bool process();
        };
 
+       enum BufferMask
+       {
+               VERTEX_BUFFER = 1,
+               INDEX_BUFFER = 2
+       };
+
        VertexArray vertices;
        std::vector<Batch> 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; }
index 57e85fc640ab1a66dc6d5a26187045507d479e1f..9e71536a52a3ef121fafa5d7ab99ef73f64cc2bf 100644 (file)
@@ -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);
index 0755eeb20ba6096d1bf524279194eb7d97940a3b..afc1450b0b490ee66e9a1ee7f369d19db7c4d390 100644 (file)
@@ -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);
index 912bc3c88a54c25a8c52199e8552a0d0a2b5eecc..68a6715d419c847c173ba56ab5cc0dac6c329878 100644 (file)
@@ -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<ProgramBlock>::iterator i=pu.blocks.begin(); i!=pu.blocks.end(); ++i)