#ifndef MSP_GL_BUFFERABLE_H_
#define MSP_GL_BUFFERABLE_H_
+#include <msp/core/noncopyable.h>
+
namespace Msp {
namespace GL {
class Buffer;
/**
-Base class for things that can store data in buffers. Supports buffer sharing.
-A dirty flag is provided for derived classes. It should be set when the data
-in the buffer is considered out of date, and is cleared by Bufferable after
-uploading fresh data to the buffer.
+Base class for things that can store data in buffers. Multiple Bufferables
+may be put in the same buffer.
+
+Derived classes should call mark_dirty() when the stored data has changed.
*/
-class Bufferable
+class Bufferable: public NonCopyable
{
public:
+ /**
+ Uploads data to the buffer asynchronously, through a memory mapping. API
+ calls are done in the constructor and desctructor, so upload_data may be
+ called from a different thread.
+ */
class AsyncUpdater
{
private:
};
private:
- Buffer *buffer;
- unsigned offset;
- Bufferable *next_in_buffer;
- Bufferable *prev_in_buffer;
- mutable bool location_dirty;
-protected:
- mutable bool dirty;
+ Buffer *buffer = 0;
+ std::size_t offset = 0;
+ Bufferable *next_in_buffer = 0;
+ Bufferable *prev_in_buffer = 0;
+ mutable bool location_dirty = false;
+ mutable bool dirty = false;
- Bufferable();
+protected:
+ Bufferable() = default;
+ Bufferable(Bufferable &&);
public:
virtual ~Bufferable();
/** Sets the buffer to use. If prev is not null, it must use the same
- buffer, and this object is inserted after it. */
- void use_buffer(Buffer *buf, Bufferable *prev = 0);
+ buffer, and this object is inserted after it.
+
+ Data is not uploaded immediately, but only when refresh() is called. */
+ void use_buffer(Buffer *, Bufferable *prev = 0);
/** Sets the buffer for the entire chain of objects. */
void change_buffer(Buffer *);
/** 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;
+ in the same chain, including any padding required by object alignment. */
+ std::size_t get_required_buffer_size() const;
/** Uploads new data into the buffer if necessary. */
- void refresh() const { if(buffer && dirty) upload_data(0); }
+ void refresh() const { if(dirty) upload_data(0); }
/** Returns an object which can be used to upload data to the buffer using
- mapped memory. */
- AsyncUpdater *refresh_async() const;
+ mapped memory. If data is not dirty, returns null. */
+ AsyncUpdater *refresh_async() const { return dirty ? new AsyncUpdater(*this) : 0; }
private:
void unlink_from_buffer();
/** Returns the buffer in which the data is stored. */
const Buffer *get_buffer() const { return buffer; }
-protected:
- /** Returns the amount of data to be stored in the buffer, in bytes. */
- virtual unsigned get_data_size() const = 0;
+ /** Returns the size of the data, in bytes. */
+ virtual std::size_t get_data_size() const = 0;
- /** Returns a pointer to the start of data in client memory. */
+protected:
+ /** Returns a pointer to the start of the data in client memory. */
virtual const void *get_data_pointer() const = 0;
- /** Returns the alignment required for the data, in bytes. The offset is
- guaranteed to be a multiple of this. */
- virtual unsigned get_alignment() const { return 1; }
+ /** Returns the alignment required for the data, in bytes. The offset
+ within the buffer is guaranteed to be a multiple of the alignment. */
+ virtual std::size_t get_alignment() const { return 1; }
/** Updates the offsets for the chain so that data from different objects
does not overlap. Should be called if either data size or alignment
changes. */
void update_offset();
- /** Returns the offset where the data should be uploaded. */
- unsigned get_offset() const { return offset; }
+ /* Indicates that the data of the bufferable has changed and should be
+ uploaded to the buffer again. */
+ void mark_dirty();
- /** Called when the target buffer or offset within it has changed. */
- virtual void location_changed(Buffer *, unsigned, unsigned) const { }
+public:
+ /** Returns the offset of the data from the beginning of the buffer. */
+ std::size_t get_offset() const { return offset; }
private:
/** Uploads data to the buffer. Receives pointer to mapped buffer memory as