mirror of
https://github.com/love2d/love.git
synced 2026-08-16 08:11:02 +02:00
SpriteBatches are a little more intelligent about how much data they upload to the GPU when they're flushed or drawn, now
--HG-- branch : minor
This commit is contained in:
@@ -48,6 +48,8 @@ SpriteBatch::SpriteBatch(Texture *texture, int size, int usage)
|
|||||||
, color(0)
|
, color(0)
|
||||||
, array_buf(0)
|
, array_buf(0)
|
||||||
, element_buf(0)
|
, element_buf(0)
|
||||||
|
, buffer_used_offset(0)
|
||||||
|
, buffer_used_size(0)
|
||||||
{
|
{
|
||||||
if (size <= 0)
|
if (size <= 0)
|
||||||
throw love::Exception("Invalid SpriteBatch size.");
|
throw love::Exception("Invalid SpriteBatch size.");
|
||||||
@@ -163,7 +165,9 @@ void SpriteBatch::clear()
|
|||||||
void SpriteBatch::flush()
|
void SpriteBatch::flush()
|
||||||
{
|
{
|
||||||
VertexBuffer::Bind bind(*array_buf);
|
VertexBuffer::Bind bind(*array_buf);
|
||||||
array_buf->unmap();
|
array_buf->unmap(buffer_used_offset, buffer_used_size);
|
||||||
|
|
||||||
|
buffer_used_offset = buffer_used_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpriteBatch::setTexture(Texture *newtexture)
|
void SpriteBatch::setTexture(Texture *newtexture)
|
||||||
@@ -222,32 +226,21 @@ void SpriteBatch::setBufferSize(int newsize)
|
|||||||
|
|
||||||
VertexBuffer *new_array_buf = nullptr;
|
VertexBuffer *new_array_buf = nullptr;
|
||||||
VertexIndex *new_element_buf = nullptr;
|
VertexIndex *new_element_buf = nullptr;
|
||||||
void *new_data = nullptr;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
new_array_buf = VertexBuffer::Create(vertex_size, array_buf->getTarget(), array_buf->getUsage());
|
new_array_buf = VertexBuffer::Create(vertex_size, array_buf->getTarget(), array_buf->getUsage());
|
||||||
new_element_buf = new VertexIndex(newsize);
|
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 &)
|
catch (love::Exception &)
|
||||||
{
|
{
|
||||||
delete new_array_buf;
|
delete new_array_buf;
|
||||||
delete new_element_buf;
|
delete new_element_buf;
|
||||||
|
|
||||||
{
|
|
||||||
VertexBuffer::Bind bind(*array_buf);
|
|
||||||
array_buf->unmap();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy as much of the old data into the new VertexBuffer as can fit.
|
// 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.
|
// We don't need to unmap the old VertexBuffer since we're deleting it.
|
||||||
delete array_buf;
|
delete array_buf;
|
||||||
@@ -259,11 +252,8 @@ void SpriteBatch::setBufferSize(int newsize)
|
|||||||
|
|
||||||
next = std::min(next, newsize);
|
next = std::min(next, newsize);
|
||||||
|
|
||||||
// But we should unmap the new one!
|
// The new VertexBuffer isn't mapped, so we should reset these variables.
|
||||||
{
|
buffer_used_offset = buffer_used_size = 0;
|
||||||
VertexBuffer::Bind bind(*new_array_buf);
|
|
||||||
new_array_buf->unmap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int SpriteBatch::getBufferSize() const
|
int SpriteBatch::getBufferSize() const
|
||||||
@@ -293,7 +283,8 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
|
|||||||
VertexBuffer::Bind element_bind(*element_buf->getVertexBuffer());
|
VertexBuffer::Bind element_bind(*element_buf->getVertexBuffer());
|
||||||
|
|
||||||
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
|
// Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.)
|
||||||
array_buf->unmap();
|
array_buf->unmap(buffer_used_offset, buffer_used_size);
|
||||||
|
buffer_used_offset = buffer_used_size = 0;
|
||||||
|
|
||||||
Color curcolor = gl.getColor();
|
Color curcolor = gl.getColor();
|
||||||
|
|
||||||
@@ -329,7 +320,7 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
|
|||||||
|
|
||||||
void SpriteBatch::addv(const Vertex *v, int index)
|
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);
|
VertexBuffer::Bind bind(*array_buf);
|
||||||
|
|
||||||
@@ -338,6 +329,9 @@ void SpriteBatch::addv(const Vertex *v, int index)
|
|||||||
array_buf->map();
|
array_buf->map();
|
||||||
|
|
||||||
array_buf->fill(index * sprite_size, sprite_size, v);
|
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)
|
void SpriteBatch::setColorv(Vertex *v, const Color &color)
|
||||||
|
|||||||
@@ -141,6 +141,10 @@ private:
|
|||||||
VertexBuffer *array_buf;
|
VertexBuffer *array_buf;
|
||||||
VertexIndex *element_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>::Entry usageHintEntries[];
|
||||||
static StringMap<UsageHint, USAGE_MAX_ENUM> usageHints;
|
static StringMap<UsageHint, USAGE_MAX_ENUM> usageHints;
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ VertexBuffer::VertexBuffer(size_t size, GLenum target, GLenum usage, MemoryBacki
|
|||||||
, is_dirty(true)
|
, is_dirty(true)
|
||||||
{
|
{
|
||||||
if (getMemoryBacking() == BACKING_FULL)
|
if (getMemoryBacking() == BACKING_FULL)
|
||||||
memory_map = malloc(getSize());
|
memory_map = (char *) malloc(getSize());
|
||||||
|
|
||||||
bool ok = load(false);
|
bool ok = load(false);
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ void *VertexBuffer::map()
|
|||||||
|
|
||||||
if (!memory_map)
|
if (!memory_map)
|
||||||
{
|
{
|
||||||
memory_map = malloc(getSize());
|
memory_map = (char *) malloc(getSize());
|
||||||
if (!memory_map)
|
if (!memory_map)
|
||||||
throw love::Exception("Out of memory (oh the humanity!)");
|
throw love::Exception("Out of memory (oh the humanity!)");
|
||||||
}
|
}
|
||||||
@@ -102,11 +102,28 @@ void *VertexBuffer::map()
|
|||||||
return memory_map;
|
return memory_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexBuffer::unmap()
|
void VertexBuffer::unmapStatic(size_t offset, size_t size)
|
||||||
|
{
|
||||||
|
// Upload the mapped data to the buffer.
|
||||||
|
glBufferSubData(getTarget(), (GLintptr) offset, (GLsizeiptr) size, memory_map + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VertexBuffer::unmapStream()
|
||||||
|
{
|
||||||
|
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
||||||
|
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
||||||
|
glBufferData(getTarget(), (GLsizeiptr) getSize(), nullptr, getUsage());
|
||||||
|
glBufferData(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
|
||||||
|
}
|
||||||
|
|
||||||
|
void VertexBuffer::unmap(size_t usedOffset, size_t usedSize)
|
||||||
{
|
{
|
||||||
if (!is_mapped)
|
if (!is_mapped)
|
||||||
return;
|
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
|
// VBO::bind is a no-op when the VBO is mapped, so we have to make sure it's
|
||||||
// bound here.
|
// bound here.
|
||||||
if (!is_bound)
|
if (!is_bound)
|
||||||
@@ -115,17 +132,23 @@ void VertexBuffer::unmap()
|
|||||||
is_bound = true;
|
is_bound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getUsage() == GL_STATIC_DRAW)
|
switch (getUsage())
|
||||||
{
|
{
|
||||||
// Upload the mapped data to the buffer.
|
case GL_STATIC_DRAW:
|
||||||
glBufferSubData(getTarget(), 0, (GLsizeiptr) getSize(), memory_map);
|
unmapStatic(usedOffset, usedSize);
|
||||||
}
|
break;
|
||||||
else
|
case GL_STREAM_DRAW:
|
||||||
{
|
unmapStream();
|
||||||
// "orphan" current buffer to avoid implicit synchronisation on the GPU:
|
break;
|
||||||
// http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf
|
case GL_DYNAMIC_DRAW:
|
||||||
glBufferData(getTarget(), (GLsizeiptr) getSize(), NULL, getUsage());
|
default:
|
||||||
glBufferData(getTarget(), (GLsizeiptr) getSize(), memory_map, getUsage());
|
// 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;
|
is_mapped = false;
|
||||||
@@ -151,7 +174,7 @@ void VertexBuffer::unbind()
|
|||||||
void VertexBuffer::fill(size_t offset, size_t size, const void *data)
|
void VertexBuffer::fill(size_t offset, size_t size, const void *data)
|
||||||
{
|
{
|
||||||
if (is_mapped || getMemoryBacking() == BACKING_FULL)
|
if (is_mapped || getMemoryBacking() == BACKING_FULL)
|
||||||
memcpy(static_cast<char *>(memory_map) + offset, data, size);
|
memcpy(memory_map + offset, data, size);
|
||||||
|
|
||||||
if (!is_mapped)
|
if (!is_mapped)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,6 +28,9 @@
|
|||||||
// OpenGL
|
// OpenGL
|
||||||
#include "OpenGL.h"
|
#include "OpenGL.h"
|
||||||
|
|
||||||
|
// C
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
namespace graphics
|
namespace graphics
|
||||||
@@ -145,8 +148,12 @@ public:
|
|||||||
* when used to draw elements.
|
* when used to draw elements.
|
||||||
*
|
*
|
||||||
* The VertexBuffer must be bound to use this function.
|
* 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();
|
virtual void unmap(size_t usedOffset = 0, size_t usedSize = -1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind the VertexBuffer to its specified target.
|
* Bind the VertexBuffer to its specified target.
|
||||||
@@ -267,6 +274,8 @@ private:
|
|||||||
*/
|
*/
|
||||||
void unload(bool save);
|
void unload(bool save);
|
||||||
|
|
||||||
|
void unmapStatic(size_t offset, size_t size);
|
||||||
|
void unmapStream();
|
||||||
|
|
||||||
// Whether the buffer is currently bound.
|
// Whether the buffer is currently bound.
|
||||||
bool is_bound;
|
bool is_bound;
|
||||||
@@ -291,7 +300,7 @@ private:
|
|||||||
|
|
||||||
// A pointer to mapped memory. Will be inialized on the first
|
// A pointer to mapped memory. Will be inialized on the first
|
||||||
// call to map().
|
// call to map().
|
||||||
void *memory_map;
|
char *memory_map;
|
||||||
|
|
||||||
// Set if the buffer was modified while operating on gpu memory
|
// Set if the buffer was modified while operating on gpu memory
|
||||||
// and needs to be synchronized.
|
// and needs to be synchronized.
|
||||||
|
|||||||
Reference in New Issue
Block a user