#include <stdexcept>
-#include <msp/core/raii.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include "buffer.h"
#include "bufferable.h"
+#include "error.h"
using namespace std;
namespace Msp {
namespace GL {
-Bufferable::Bufferable():
- buffer(0),
- offset(0),
- next_in_buffer(0),
- prev_in_buffer(0),
- location_dirty(false),
- dirty(false)
-{ }
+Bufferable::Bufferable(Bufferable &&other):
+ buffer(other.buffer),
+ offset(other.offset),
+ next_in_buffer(other.next_in_buffer),
+ prev_in_buffer(other.prev_in_buffer),
+ location_dirty(other.location_dirty),
+ dirty(other.dirty)
+{
+ other.buffer = 0;
+ other.next_in_buffer = 0;
+ other.prev_in_buffer = 0;
+ if(next_in_buffer)
+ next_in_buffer->prev_in_buffer = this;
+ if(prev_in_buffer)
+ prev_in_buffer->next_in_buffer = this;
+}
Bufferable::~Bufferable()
{
}
location_dirty = true;
- dirty = true;
+ mark_dirty();
update_offset();
}
{
b->buffer = buf;
b->location_dirty = true;
- b->dirty = true;
+ b->mark_dirty();
}
for(Bufferable *b=prev_in_buffer; b; b=b->prev_in_buffer)
{
b->buffer = buf;
b->location_dirty = true;
- b->dirty = true;
+ b->mark_dirty();
}
}
-unsigned Bufferable::get_required_buffer_size() const
+size_t Bufferable::get_required_buffer_size(bool align) 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;
+ size_t size = last->offset+last->get_data_size();
+ if(align)
+ {
+ const Bufferable *first = this;
+ for(; first->prev_in_buffer; first=first->prev_in_buffer) ;
+ size_t alignment = first->get_alignment();
+ size += alignment-1;
+ size -= size%alignment;
+ }
+ return size;
}
void Bufferable::unlink_from_buffer()
void Bufferable::update_offset()
{
- unsigned new_offset = 0;
+ size_t new_offset = 0;
if(prev_in_buffer)
new_offset = prev_in_buffer->offset+prev_in_buffer->get_data_size();
- unsigned align = get_alignment();
+ size_t align = get_alignment();
new_offset += align-1;
new_offset -= new_offset%align;
if(new_offset!=offset)
{
offset = new_offset;
location_dirty = true;
- dirty = true;
+ mark_dirty();
}
if(next_in_buffer)
else if(buffer && offset+get_data_size()>buffer->get_size())
{
location_dirty = true;
- dirty = true;
+ mark_dirty();
}
}
-void Bufferable::upload_data(char *target) const
+void Bufferable::mark_dirty()
+{
+ dirty = 0xFF;
+}
+
+void Bufferable::upload_data(unsigned frame, char *target) const
{
- unsigned data_size = get_data_size();
+ if(!buffer)
+ throw invalid_operation("Bufferable::upload_data");
+
+ unsigned multi_buf = buffer->get_multiplicity();
+ frame %= multi_buf;
+ uint8_t mask = 1<<frame;
+ if(!(dirty&mask))
+ return;
+
+ size_t data_size = get_data_size();
if(location_dirty)
{
buffer->require_size(offset+data_size);
- location_changed(buffer, offset, data_size);
location_dirty = false;
}
copy(source, source+data_size, target);
}
else
- buffer->sub_data(offset, data_size, get_data_pointer());
- dirty = false;
+ buffer->sub_data(frame*buffer->get_size()+offset, data_size, get_data_pointer());
+ dirty &= ~mask;
+ if(!(dirty&((1<<multi_buf)-1)))
+ dirty = 0;
}
-
-Bufferable::AsyncUpdater::AsyncUpdater(const Bufferable &b):
- bufferable(b)
+Bufferable::AsyncUpdater *Bufferable::create_async_updater() const
{
- bufferable.buffer->require_size(bufferable.get_required_buffer_size());
- mapped_address = reinterpret_cast<char *>(bufferable.buffer->map());
+ if(!buffer || buffer->get_multiplicity()>1)
+ throw invalid_operation("Bufferable::create_async_updater");
+ return new AsyncUpdater(*this);
}
-Bufferable::AsyncUpdater::~AsyncUpdater()
-{
- bufferable.buffer->unmap();
-}
+
+Bufferable::AsyncUpdater::AsyncUpdater(const Bufferable &b):
+ bufferable(b),
+ transfer(b.buffer->sub_data_async(0, bufferable.get_required_buffer_size()))
+{ }
void Bufferable::AsyncUpdater::upload_data()
{
- bufferable.upload_data(mapped_address+bufferable.offset);
+ char *mapped_address = static_cast<char *>(transfer.get_address());
+ bufferable.upload_data(0, 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)
- b->upload_data(mapped_address+b->offset);
+ b->upload_data(0, mapped_address+b->offset);
for(const Bufferable *b=bufferable.next_in_buffer; b; b=b->next_in_buffer)
if(b->dirty)
- b->upload_data(mapped_address+b->offset);
+ b->upload_data(0, mapped_address+b->offset);
}
} // namespace GL