Backported commits 145524a and 7785b4b from minor into default (Added SpriteBatch:flush, deprecated SpriteBatch:bind/unbind, SpriteBatch:flush is called implicitly when the spritebatch is drawn.)

This commit is contained in:
Alex Szpakowski
2014-06-17 21:52:56 -03:00
parent b9cd93e071
commit f0fa63ba47
6 changed files with 104 additions and 84 deletions
+33 -26
View File
@@ -46,8 +46,10 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, int usage)
, size(size)
, next(0)
, color(0)
, array_buf(0)
, element_buf(0)
, array_buf(nullptr)
, element_buf(nullptr)
, buffer_used_offset(0)
, buffer_used_size(0)
{
if (size <= 0)
throw love::Exception("Invalid SpriteBatch size.");
@@ -160,18 +162,12 @@ void SpriteBatch::clear()
next = 0;
}
void *SpriteBatch::lock()
void SpriteBatch::flush()
{
VertexBuffer::Bind bind(*array_buf);
array_buf->unmap(buffer_used_offset, buffer_used_size);
return array_buf->map();
}
void SpriteBatch::unlock()
{
VertexBuffer::Bind bind(*array_buf);
array_buf->unmap();
buffer_used_offset = buffer_used_size = 0;
}
void SpriteBatch::setTexture(Texture *newtexture)
@@ -220,33 +216,31 @@ void SpriteBatch::setBufferSize(int newsize)
return;
// Map (lock) the old VertexBuffer to get a pointer to its data.
void *old_data = lock();
void *old_data = nullptr;
{
VertexBuffer::Bind bind(*array_buf);
old_data = array_buf->map();
}
size_t vertex_size = sizeof(Vertex) * 4 * newsize;
VertexBuffer *new_array_buf = 0;
VertexIndex *new_element_buf = 0;
void *new_data = 0;
VertexBuffer *new_array_buf = nullptr;
VertexIndex *new_element_buf = nullptr;
try
{
new_array_buf = VertexBuffer::Create(vertex_size, array_buf->getTarget(), array_buf->getUsage());
new_element_buf = new VertexIndex(newsize);
// VBO::map can throw an exception. Also we want to scope the bind.
VertexBuffer::Bind bind(*new_array_buf);
new_data = new_array_buf->map();
}
catch (love::Exception &)
{
delete new_array_buf;
delete new_element_buf;
unlock();
throw;
}
// Copy as much of the old data into the new VertexBuffer as can fit.
memcpy(new_data, old_data, sizeof(Vertex) * 4 * std::min(newsize, size));
new_array_buf->fill(0, sizeof(Vertex) * 4 * std::min(newsize, size), old_data);
// We don't need to unmap the old VertexBuffer since we're deleting it.
delete array_buf;
@@ -258,8 +252,8 @@ void SpriteBatch::setBufferSize(int newsize)
next = std::min(next, newsize);
// But we should unmap (unlock) the new one!
unlock();
// The new VertexBuffer isn't mapped, so we should reset these variables.
buffer_used_offset = buffer_used_size = 0;
}
int SpriteBatch::getBufferSize() const
@@ -269,7 +263,7 @@ int SpriteBatch::getBufferSize() const
void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float ox, float oy, float kx, float ky)
{
const size_t vertex_offset = offsetof(Vertex, x);
const size_t pos_offset = offsetof(Vertex, x);
const size_t texel_offset = offsetof(Vertex, s);
const size_t color_offset = offsetof(Vertex, r);
@@ -288,6 +282,10 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
VertexBuffer::Bind array_bind(*array_buf);
VertexBuffer::Bind element_bind(*element_buf->getVertexBuffer());
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
array_buf->unmap(buffer_used_offset, buffer_used_size);
buffer_used_offset = buffer_used_size = 0;
Color curcolor = gl.getColor();
// Apply per-sprite color, if a color is set.
@@ -298,7 +296,7 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
}
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(vertex_offset));
glVertexPointer(2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(pos_offset));
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(texel_offset));
@@ -322,9 +320,18 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
void SpriteBatch::addv(const Vertex *v, int index)
{
static const int sprite_size = 4 * sizeof(Vertex); // bytecount
static const size_t sprite_size = 4 * sizeof(Vertex); // bytecount
VertexBuffer::Bind bind(*array_buf);
// Always keep the VBO mapped when adding data for now (it'll be unmapped
// on draw.)
array_buf->map();
array_buf->fill(index * sprite_size, sprite_size, v);
buffer_used_offset = std::min(buffer_used_offset, index * sprite_size);
buffer_used_size = std::max(buffer_used_size, (index + 1) * sprite_size - buffer_used_offset);
}
void SpriteBatch::setColorv(Vertex *v, const Color &color)
+5 -2
View File
@@ -65,8 +65,7 @@ public:
int addq(Quad *quad, float x, float y, float a, float sx, float sy, float ox, float oy, float kx, float ky, int index = -1);
void clear();
void *lock();
void unlock();
void flush();
void setTexture(Texture *newtexture);
Texture *getTexture();
@@ -142,6 +141,10 @@ private:
VertexBuffer *array_buf;
VertexIndex *element_buf;
// The portion of the vertex buffer that's been modified while mapped.
size_t buffer_used_offset;
size_t buffer_used_size;
static StringMap<UsageHint, USAGE_MAX_ENUM>::Entry usageHintEntries[];
static StringMap<UsageHint, USAGE_MAX_ENUM> usageHints;
+41 -44
View File
@@ -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();
+14 -4
View File
@@ -28,6 +28,9 @@
// OpenGL
#include "OpenGL.h"
// C
#include <stddef.h>
namespace love
{
namespace graphics
@@ -152,8 +155,12 @@ public:
* when used to draw elements.
*
* The VertexBuffer must be bound to use this function.
*
* @param usedOffset The offset into the mapped buffer indicating the
* sub-range of data modified. Optional.
* @param usedSize The size of the sub-range of modified data. Optional.
*/
virtual void unmap() = 0;
virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1) = 0;
/**
* Bind the VertexBuffer to its specified target.
@@ -294,7 +301,7 @@ public:
// Implements VertexBuffer.
virtual void *map();
virtual void unmap();
virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1);
virtual void bind();
virtual void unbind();
virtual void fill(size_t offset, size_t size, const void *data);
@@ -328,7 +335,7 @@ public:
// Implements VertexBuffer.
virtual void *map();
virtual void unmap();
virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1);
virtual void bind();
virtual void unbind();
virtual void fill(size_t offset, size_t size, const void *data);
@@ -355,12 +362,15 @@ private:
*/
void unload(bool save);
void unmapStatic(size_t offset, size_t size);
void unmapStream();
// The VBO identifier. Assigned by OpenGL.
GLuint vbo;
// A pointer to mapped memory. Will be inialized on the first
// call to map().
void *memory_map;
char *memory_map;
// Set if the buffer was modified while operating on gpu memory
// and needs to be synchronized.
@@ -118,17 +118,16 @@ int w_SpriteBatch_clear(lua_State *L)
return 0;
}
int w_SpriteBatch_bind(lua_State *L)
int w_SpriteBatch_bind(lua_State* /*L*/)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
luax_catchexcept(L, [&](){ t->lock(); });
// No-op (deprecated.)
return 0;
}
int w_SpriteBatch_unbind(lua_State *L)
int w_SpriteBatch_flush(lua_State *L)
{
SpriteBatch *t = luax_checkspritebatch(L, 1);
t->unlock();
t->flush();
return 0;
}
@@ -239,8 +238,7 @@ static const luaL_Reg functions[] =
{ "add", w_SpriteBatch_add },
{ "set", w_SpriteBatch_set },
{ "clear", w_SpriteBatch_clear },
{ "bind", w_SpriteBatch_bind },
{ "unbind", w_SpriteBatch_unbind },
{ "flush", w_SpriteBatch_flush },
{ "setTexture", w_SpriteBatch_setTexture },
{ "getTexture", w_SpriteBatch_getTexture },
{ "setColor", w_SpriteBatch_setColor },
@@ -252,6 +250,11 @@ static const luaL_Reg functions[] =
// Deprecated since 0.9.1.
{ "setImage", w_SpriteBatch_setTexture },
{ "getImage", w_SpriteBatch_getTexture },
// Deprecated since 0.9.2.
{ "bind", w_SpriteBatch_bind },
{ "unbind", w_SpriteBatch_flush },
{ 0, 0 }
};
@@ -38,7 +38,7 @@ int w_SpriteBatch_set(lua_State *L);
int w_SpriteBatch_setg(lua_State *L);
int w_SpriteBatch_clear(lua_State *L);
int w_SpriteBatch_bind(lua_State *L);
int w_SpriteBatch_unbind(lua_State *L);
int w_SpriteBatch_flush(lua_State *L);
int w_SpriteBatch_setTexture(lua_State *L);
int w_SpriteBatch_getTexture(lua_State *L);
int w_SpriteBatch_setColor(lua_State *L);