Bufferable::Bufferable():
buffer(0),
- buffer_offset(0),
+ offset(0),
next_in_buffer(0),
prev_in_buffer(0),
dirty(false)
{
prev_in_buffer = prev;
if(prev_in_buffer)
+ {
+ next_in_buffer = prev_in_buffer->next_in_buffer;
prev_in_buffer->next_in_buffer = this;
+ }
}
- update_buffer_offset();
+ update_offset();
}
void Bufferable::unlink_from_buffer()
if(next_in_buffer)
{
next_in_buffer->prev_in_buffer = prev_in_buffer;
- next_in_buffer->update_buffer_offset();
+ next_in_buffer->update_offset();
}
prev_in_buffer = 0;
next_in_buffer = 0;
+ buffer = 0;
}
-void Bufferable::update_buffer_offset()
+void Bufferable::update_offset()
{
- unsigned offset = 0;
+ unsigned new_offset = 0;
if(prev_in_buffer)
- offset = prev_in_buffer->buffer_offset+prev_in_buffer->get_data_size();
+ new_offset = prev_in_buffer->offset+prev_in_buffer->get_data_size();
unsigned align = get_alignment();
- offset += align-1;
- offset -= offset%align;
- if(offset!=buffer_offset)
+ new_offset += align-1;
+ new_offset -= new_offset%align;
+ if(new_offset!=offset)
{
- buffer_offset = offset;
+ offset = new_offset;
dirty = true;
offset_changed();
}
if(next_in_buffer)
- next_in_buffer->update_buffer_offset();
+ next_in_buffer->update_offset();
- /* Do not resize the buffer here, as the offsets may change multiple times
- before the buffer is actually used */
+ /* Do not resize the buffer here, as more bufferables may be added before
+ the buffer is actually used. */
}
-void Bufferable::update_buffer_data() const
+void Bufferable::update_buffer() const
{
- if(buffer_offset+get_data_size()>=buffer->get_size())
+ 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->buffer_offset+last->get_data_size();
+ unsigned total_size = last->offset+last->get_data_size();
if(total_size>buffer->get_size())
{
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.
+*/
class Bufferable
{
-protected:
+private:
Buffer *buffer;
- unsigned buffer_offset;
+ unsigned offset;
Bufferable *next_in_buffer;
Bufferable *prev_in_buffer;
+protected:
mutable bool dirty;
+protected:
Bufferable();
public:
virtual ~Bufferable();
- void use_buffer(Buffer *, Bufferable * = 0);
+ /** 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);
+
private:
void unlink_from_buffer();
protected:
+ /** Returns the buffer in which the data is stored. */
+ Buffer *get_buffer() const { return buffer; }
+
+ /** Returns the amount of data to be stored in the buffer, in bytes. */
virtual unsigned get_data_size() 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; }
- void update_buffer_offset();
+ /** 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; }
+
+ /** Called when the offset for the data has changed. */
virtual void offset_changed() { }
- void update_buffer_data() const;
+ /** Resizes the buffer if necessary and calls upload_data(). */
+ void update_buffer() const;
+
+ /** Uploads data to the buffer. */
virtual void upload_data() const = 0;
};
void UniformBlock::upload_data() const
{
if(!buf_range)
- buf_range = new BufferRange(*buffer, buffer_offset, size);
+ buf_range = new BufferRange(*get_buffer(), get_offset(), size);
buf_range->data(&data[0]);
}
void UniformBlock::attach(const Program::UniformInfo &info, const Uniform &uni)
{
uniforms[info.location] = &uni;
- if(buffer)
+ if(get_buffer())
{
uni.store(info, &data[info.location]);
dirty = true;
void UniformBlock::apply(int index) const
{
- if((index>=0) != (buffer!=0))
+ if((index>=0) != (get_buffer()!=0))
throw invalid_operation("UniformBlock::apply");
- if(buffer)
+ if(get_buffer())
{
if(dirty)
- update_buffer_data();
+ update_buffer();
buf_range->bind_to(UNIFORM_BUFFER, index);
}
else