From 23e712a7300e6c6f4c0c6ee157d222f12ab77c25 Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Sun, 13 Oct 2013 22:00:38 +0200 Subject: [PATCH] Tilemap: Draw consecutive scanrows in one draw call If consecutive scanrows in the scene list have no foreign elements in between them, we batch them up and draw them in one glDrawElements() call. This should reduce the Tilemap induced draw calls on average by at least 50 percent. --- src/scene.h | 2 ++ src/tilemap.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/scene.h b/src/scene.h index fca0cd3..3ba8ddc 100644 --- a/src/scene.h +++ b/src/scene.h @@ -31,6 +31,7 @@ class SceneElement; class Viewport; class Window; class ScanRow; +struct TilemapPrivate; class Scene { @@ -99,6 +100,7 @@ protected: friend class Scene; friend class Viewport; + friend struct TilemapPrivate; }; #endif // SCENE_H diff --git a/src/tilemap.cpp b/src/tilemap.cpp index efe48e4..004934f 100644 --- a/src/tilemap.cpp +++ b/src/tilemap.cpp @@ -224,6 +224,14 @@ struct ScanRow : public ViewportElement GLsizei vboCount; TilemapPrivate *p; + /* If this row is part of a batch and not + * the head, it is 'muted' via this flag */ + bool batchedFlag; + + /* If this row is a batch head, this variable + * holds the element count of the entire batch */ + GLsizei vboBatchCount; + ScanRow(TilemapPrivate *p, Viewport *viewport, int index); void draw(); @@ -1022,6 +1030,47 @@ struct TilemapPrivate } } + /* When there are two or more scanrows with no other + * elements between them in the scene list, we can + * render them in a batch (as the scanrow data itself + * is ordered sequentially in VRAM). Every frame, we + * scan the scene list for such sequential rows and + * batch them up for drawing. The first row of the batch + * (the "batch head") executes the draw call, all others + * are muted via the 'batchedFlag'. For simplicity, + * single sized batches are possible. */ + void prepareScanrowBatches() + { + const QVector &scanrows = elem.scanrows; + + for (int i = 0; i < scanrows.size(); ++i) + { + ScanRow *batchHead = scanrows[i]; + batchHead->batchedFlag = false; + + GLsizei vboBatchCount = batchHead->vboCount; + IntruListLink *iter = &batchHead->link; + + for (i = i+1; i < scanrows.size(); ++i) + { + iter = iter->next; + ScanRow *row = scanrows[i]; + + /* Check if the next SceneElement is also + * the next scanrow in our list. If not, + * the current batch is complete */ + if (iter != &row->link) + break; + + vboBatchCount += row->vboCount; + row->batchedFlag = true; + } + + batchHead->vboBatchCount = vboBatchCount; + --i; + } + } + void prepare() { if (!verifyResources()) @@ -1064,6 +1113,8 @@ struct TilemapPrivate zOrderDirty = false; } + prepareScanrowBatches(); + tilemapReady = true; } }; @@ -1152,7 +1203,8 @@ void GroundLayer::onGeometryChange(const Scene::Geometry &geo) ScanRow::ScanRow(TilemapPrivate *p, Viewport *viewport, int index) : ViewportElement(viewport, 32 + index*32, p->elem.scanrowStamp), index(index), - p(p) + p(p), + vboBatchCount(0) { vboOffset = p->scanrowBases[index] * sizeof(uint32_t) * 6; vboCount = p->scanrowSize(index) * 6; @@ -1160,6 +1212,9 @@ ScanRow::ScanRow(TilemapPrivate *p, Viewport *viewport, int index) void ScanRow::draw() { + if (batchedFlag) + return; + SimpleShader &shader = shState->simpleShader(); shader.bind(); shader.applyViewportProj(); @@ -1187,7 +1242,7 @@ void ScanRow::draw() void ScanRow::drawInt() { - glDrawElements(GL_TRIANGLES, vboCount, + glDrawElements(GL_TRIANGLES, vboBatchCount, GL_UNSIGNED_INT, (GLvoid*) (vboOffset + p->tiles.frameIdx * p->tiles.bufferFrameSize)); }