|
@@ -89,7 +89,7 @@ void *VertexArray::map()
|
|
|
return buf;
|
|
|
}
|
|
|
|
|
|
-void VertexArray::unmap()
|
|
|
+void VertexArray::unmap(size_t /*usedOffset*/, size_t /*usedSize*/)
|
|
|
{
|
|
|
is_mapped = false;
|
|
|
}
|
|
@@ -119,14 +119,14 @@ const void *VertexArray::getPointer(size_t offset) const
|
|
|
VBO::VBO(size_t size, GLenum target, GLenum usage, MemoryBacking backing)
|
|
|
: VertexBuffer(size, target, usage, backing)
|
|
|
, vbo(0)
|
|
|
- , memory_map(0)
|
|
|
+ , memory_map(nullptr)
|
|
|
, is_dirty(true)
|
|
|
{
|
|
|
if (!(GLEE_ARB_vertex_buffer_object || GLEE_VERSION_1_5))
|
|
|
throw love::Exception("Not supported");
|
|
|
|
|
|
if (getMemoryBacking() == BACKING_FULL)
|
|
|
- memory_map = malloc(getSize());
|
|
|
+ memory_map = (char *) malloc(getSize());
|
|
|
|
|
|
bool ok = load(false);
|
|
|
|
|
@@ -153,7 +153,7 @@ void *VBO::map()
|
|
|
|
|
|
if (!memory_map)
|
|
|
{
|
|
|
- memory_map = malloc(getSize());
|
|
|
+ memory_map = (char * ) malloc(getSize());
|
|
|
if (!memory_map)
|
|
|
throw love::Exception("Out of memory (oh the humanity!)");
|
|
|
}
|
|
@@ -169,11 +169,28 @@ void *VBO::map()
|
|
|
return memory_map;
|
|
|
}
|
|
|
|
|
|
-void VBO::unmap()
|
|
|
+void VBO::unmapStatic(size_t offset, size_t size)
|
|
|
+{
|
|
|
+ // Upload the mapped data to the buffer.
|
|
|
+ glBufferSubDataARB(getTarget(), (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
|
|
|
+}
|
|
|
+
|
|
|
+void VBO::unmapStream()
|
|
|
+{
|
|
|
+ // "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
|
|
+ // http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
|
|
+ glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), nullptr, getUsage());
|
|
|
+ glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
|
|
|
+}
|
|
|
+
|
|
|
+void VBO::unmap(size_t usedOffset, size_t usedSize)
|
|
|
{
|
|
|
if (!is_mapped)
|
|
|
return;
|
|
|
|
|
|
+ usedOffset = std::min(usedOffset, getSize());
|
|
|
+ usedSize = std::min(usedSize, getSize() - usedOffset);
|
|
|
+
|
|
|
// VBO::bind is a no-op when the VBO is mapped, so we have to make sure it's
|
|
|
// bound here.
|
|
|
if (!is_bound)
|
|
@@ -182,17 +199,23 @@ void VBO::unmap()
|
|
|
is_bound = true;
|
|
|
}
|
|
|
|
|
|
- if (getUsage() == GL_STATIC_DRAW)
|
|
|
+ switch (getUsage())
|
|
|
{
|
|
|
- // Upload the mapped data to the buffer.
|
|
|
- glBufferSubDataARB(getTarget(), 0, (GLsizeiptr) getSize(), memory_map);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
|
|
- // http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
|
|
- glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), NULL, getUsage());
|
|
|
- glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
|
|
|
+ case GL_STATIC_DRAW:
|
|
|
+ unmapStatic(usedOffset, usedSize);
|
|
|
+ break;
|
|
|
+ case GL_STREAM_DRAW:
|
|
|
+ unmapStream();
|
|
|
+ break;
|
|
|
+ case GL_DYNAMIC_DRAW:
|
|
|
+ default:
|
|
|
+ // It's probably more efficient to treat it like a streaming buffer if
|
|
|
+ // more than a third of its contents have been modified during the map().
|
|
|
+ if (usedSize >= getSize() / 3)
|
|
|
+ unmapStream();
|
|
|
+ else
|
|
|
+ unmapStatic(usedOffset, usedSize);
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
is_mapped = false;
|
|
@@ -218,31 +241,11 @@ void VBO::unbind()
|
|
|
void VBO::fill(size_t offset, size_t size, const void *data)
|
|
|
{
|
|
|
if (is_mapped || getMemoryBacking() == BACKING_FULL)
|
|
|
- memcpy(static_cast<char *>(memory_map) + offset, data, size);
|
|
|
+ memcpy(memory_map + offset, data, size);
|
|
|
|
|
|
if (!is_mapped)
|
|
|
{
|
|
|
- // Not all systems have access to some faster paths...
|
|
|
- if (GLEE_APPLE_flush_buffer_range)
|
|
|
- {
|
|
|
- void *mapdata = glMapBufferARB(getTarget(), GL_WRITE_ONLY);
|
|
|
-
|
|
|
- if (mapdata)
|
|
|
- {
|
|
|
- // We specified in VBO::load that we'll do manual flushing.
|
|
|
- // Now we tell the driver it only needs to deal with the data
|
|
|
- // we changed.
|
|
|
- memcpy(static_cast<char *>(mapdata) + offset, data, size);
|
|
|
- glFlushMappedBufferRangeAPPLE(getTarget(), (GLintptr) offset, (GLsizei) size);
|
|
|
- }
|
|
|
-
|
|
|
- glUnmapBufferARB(getTarget());
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // Fall back to a possibly slower SubData (more chance of syncing.)
|
|
|
- glBufferSubDataARB(getTarget(), (GLintptr) offset, (GLsizeiptr) size, data);
|
|
|
- }
|
|
|
+ glBufferSubDataARB(getTarget(), (GLintptr) offset, (GLsizeiptr) size, data);
|
|
|
|
|
|
if (getMemoryBacking() != BACKING_FULL)
|
|
|
is_dirty = true;
|
|
@@ -271,17 +274,11 @@ bool VBO::load(bool restore)
|
|
|
VertexBuffer::Bind bind(*this);
|
|
|
|
|
|
// Copy the old buffer only if 'restore' was requested.
|
|
|
- const GLvoid *src = restore ? memory_map : 0;
|
|
|
+ const GLvoid *src = restore ? memory_map : nullptr;
|
|
|
|
|
|
while (GL_NO_ERROR != glGetError())
|
|
|
/* clear error messages */;
|
|
|
|
|
|
- // We don't want to flush the entire buffer when we just modify a small
|
|
|
- // portion of it (VBO::fill without VBO::map), so we'll handle the flushing
|
|
|
- // ourselves when we can.
|
|
|
- if (GLEE_APPLE_flush_buffer_range)
|
|
|
- glBufferParameteriAPPLE(getTarget(), GL_BUFFER_FLUSHING_UNMAP_APPLE, GL_FALSE);
|
|
|
-
|
|
|
// Note that if 'src' is '0', no data will be copied.
|
|
|
glBufferDataARB(getTarget(), (GLsizeiptr) getSize(), src, getUsage());
|
|
|
GLenum err = glGetError();
|