mirror of
https://github.com/love2d/love.git
synced 2026-08-19 12:14:20 +02:00
Simplify OpenGL 3+ stream buffer syncing code used with automatic batching.
This commit is contained in:
@@ -36,8 +36,9 @@ namespace graphics
|
|||||||
namespace opengl
|
namespace opengl
|
||||||
{
|
{
|
||||||
|
|
||||||
static const int BUFFER_FRAMES = 3;
|
// Typically this should be 3 frames, but we only do per-frame syncing right now
|
||||||
static const int MAX_SYNCS_PER_FRAME = 4;
|
// so we add an extra frame to reduce the (small) chance of stalls.
|
||||||
|
static const int BUFFER_FRAMES = 4;
|
||||||
|
|
||||||
class StreamBufferClientMemory final : public love::graphics::StreamBuffer
|
class StreamBufferClientMemory final : public love::graphics::StreamBuffer
|
||||||
{
|
{
|
||||||
@@ -184,7 +185,6 @@ public:
|
|||||||
|
|
||||||
StreamBufferSync(BufferType type, size_t size)
|
StreamBufferSync(BufferType type, size_t size)
|
||||||
: love::graphics::StreamBuffer(type, size)
|
: love::graphics::StreamBuffer(type, size)
|
||||||
, syncSize((size + MAX_SYNCS_PER_FRAME - 1) / MAX_SYNCS_PER_FRAME)
|
|
||||||
, frameIndex(0)
|
, frameIndex(0)
|
||||||
, syncs()
|
, syncs()
|
||||||
{}
|
{}
|
||||||
@@ -193,7 +193,9 @@ public:
|
|||||||
|
|
||||||
void nextFrame() override
|
void nextFrame() override
|
||||||
{
|
{
|
||||||
getCurrentSync()->fence();
|
// Insert a GPU fence for this frame's section of the data, we'll wait
|
||||||
|
// for it when we try to map that data for writing in subsequent frames.
|
||||||
|
syncs[frameIndex].fence();
|
||||||
|
|
||||||
frameIndex = (frameIndex + 1) % BUFFER_FRAMES;
|
frameIndex = (frameIndex + 1) % BUFFER_FRAMES;
|
||||||
frameGPUReadOffset = 0;
|
frameGPUReadOffset = 0;
|
||||||
@@ -201,30 +203,16 @@ public:
|
|||||||
|
|
||||||
void markUsed(size_t usedsize) override
|
void markUsed(size_t usedsize) override
|
||||||
{
|
{
|
||||||
int firstSyncIndex = frameGPUReadOffset / syncSize;
|
// We insert a fence for all data from this frame at the end of the
|
||||||
int lastSyncIndex = std::min((frameGPUReadOffset + usedsize), bufferSize - 1) / syncSize;
|
// frame (in nextFrame), rather than doing anything more fine-grained.
|
||||||
|
|
||||||
// Insert fences for all sync buckets completely filled by this section
|
|
||||||
// of the data. The last bucket before the end of the frame will also be
|
|
||||||
// handled by nextFrame().
|
|
||||||
for (int i = firstSyncIndex; i < lastSyncIndex; i++)
|
|
||||||
syncs[frameIndex * MAX_SYNCS_PER_FRAME + i].fence();
|
|
||||||
|
|
||||||
frameGPUReadOffset += usedsize;
|
frameGPUReadOffset += usedsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
const size_t syncSize;
|
|
||||||
|
|
||||||
int frameIndex;
|
int frameIndex;
|
||||||
|
FenceSync syncs[BUFFER_FRAMES];
|
||||||
FenceSync syncs[MAX_SYNCS_PER_FRAME * BUFFER_FRAMES];
|
|
||||||
|
|
||||||
FenceSync *getCurrentSync()
|
|
||||||
{
|
|
||||||
return &syncs[frameIndex * MAX_SYNCS_PER_FRAME + frameGPUReadOffset / syncSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // StreamBufferSync
|
}; // StreamBufferSync
|
||||||
|
|
||||||
@@ -249,18 +237,12 @@ public:
|
|||||||
{
|
{
|
||||||
gl.bindBuffer(mode, vbo);
|
gl.bindBuffer(mode, vbo);
|
||||||
|
|
||||||
|
// Make sure this frame's section of the buffer is done being used.
|
||||||
|
syncs[frameIndex].cpuWait();
|
||||||
|
|
||||||
MapInfo info;
|
MapInfo info;
|
||||||
info.size = bufferSize - frameGPUReadOffset;
|
info.size = bufferSize - frameGPUReadOffset;
|
||||||
|
|
||||||
int firstSyncIndex = frameGPUReadOffset / syncSize;
|
|
||||||
int lastSyncIndex = (bufferSize - 1) / syncSize;
|
|
||||||
|
|
||||||
// We're mapping the full range of space left in the buffer, so we
|
|
||||||
// need to wait on all of it...
|
|
||||||
// FIXME: is it even worth it to have multiple sync objects per frame?
|
|
||||||
for (int i = firstSyncIndex; i <= lastSyncIndex; i++)
|
|
||||||
syncs[frameIndex * MAX_SYNCS_PER_FRAME + i].cpuWait();
|
|
||||||
|
|
||||||
GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
|
GLbitfield flags = GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
|
||||||
|
|
||||||
size_t mapoffset = (frameIndex * bufferSize) + frameGPUReadOffset;
|
size_t mapoffset = (frameIndex * bufferSize) + frameGPUReadOffset;
|
||||||
@@ -334,19 +316,12 @@ public:
|
|||||||
|
|
||||||
MapInfo map(size_t /*minsize*/) override
|
MapInfo map(size_t /*minsize*/) override
|
||||||
{
|
{
|
||||||
|
// Make sure this frame's section of the buffer is done being used.
|
||||||
|
syncs[frameIndex].cpuWait();
|
||||||
|
|
||||||
MapInfo info;
|
MapInfo info;
|
||||||
info.size = bufferSize - frameGPUReadOffset;
|
info.size = bufferSize - frameGPUReadOffset;
|
||||||
info.data = data + (frameIndex * bufferSize) + frameGPUReadOffset;
|
info.data = data + (frameIndex * bufferSize) + frameGPUReadOffset;
|
||||||
|
|
||||||
int firstSyncIndex = frameGPUReadOffset / syncSize;
|
|
||||||
int lastSyncIndex = (bufferSize - 1) / syncSize;
|
|
||||||
|
|
||||||
// We're mapping the full range of space left in the buffer, so we
|
|
||||||
// need to wait on all of it...
|
|
||||||
// FIXME: is it even worth it to have multiple sync objects per frame?
|
|
||||||
for (int i = firstSyncIndex; i <= lastSyncIndex; i++)
|
|
||||||
syncs[frameIndex * MAX_SYNCS_PER_FRAME + i].cpuWait();
|
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,19 +407,12 @@ public:
|
|||||||
|
|
||||||
MapInfo map(size_t /*minsize*/) override
|
MapInfo map(size_t /*minsize*/) override
|
||||||
{
|
{
|
||||||
|
// Make sure this frame's section of the buffer is done being used.
|
||||||
|
syncs[frameIndex].cpuWait();
|
||||||
|
|
||||||
MapInfo info;
|
MapInfo info;
|
||||||
info.size = bufferSize - frameGPUReadOffset;
|
info.size = bufferSize - frameGPUReadOffset;
|
||||||
info.data = data + (frameIndex * bufferSize) + frameGPUReadOffset;
|
info.data = data + (frameIndex * bufferSize) + frameGPUReadOffset;
|
||||||
|
|
||||||
int firstSyncIndex = frameGPUReadOffset / syncSize;
|
|
||||||
int lastSyncIndex = (bufferSize - 1) / syncSize;
|
|
||||||
|
|
||||||
// We're mapping the full range of space left in the buffer, so we
|
|
||||||
// need to wait on all of it...
|
|
||||||
// FIXME: is it even worth it to have multiple sync objects per frame?
|
|
||||||
for (int i = firstSyncIndex; i <= lastSyncIndex; i++)
|
|
||||||
syncs[frameIndex * MAX_SYNCS_PER_FRAME + i].cpuWait();
|
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user