summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2015-04-23 11:39:12 +0200
committerKonstantin Käfer <mail@kkaefer.com>2015-05-04 17:33:05 +0200
commit97a22b6f5fea5666025189231f5fecc4946ceb0b (patch)
tree7aae1422c631d92c01c4deb91b7d128a64f2df2c /src
parent28b9b3f9d64207a03b6125b3e299efda59952a03 (diff)
downloadqtlocation-mapboxgl-97a22b6f5fea5666025189231f5fecc4946ceb0b.tar.gz
upload all GL objects before rendering a frame
This moves all texture updates and buffer uploads to the beginning of every frame, before we issue any drawing commands. This means the GPU typically has much more time to upload the data in the background until we actually need it, and doesn't have to pause to store/load the framebuffer.
Diffstat (limited to 'src')
-rw-r--r--src/mbgl/geometry/buffer.hpp1
-rw-r--r--src/mbgl/geometry/glyph_atlas.cpp6
-rw-r--r--src/mbgl/geometry/glyph_atlas.hpp5
-rw-r--r--src/mbgl/geometry/line_atlas.cpp6
-rw-r--r--src/mbgl/geometry/line_atlas.hpp5
-rw-r--r--src/mbgl/geometry/sprite_atlas.cpp6
-rw-r--r--src/mbgl/geometry/sprite_atlas.hpp7
-rw-r--r--src/mbgl/renderer/debug_bucket.cpp3
-rw-r--r--src/mbgl/renderer/fill_bucket.cpp8
-rw-r--r--src/mbgl/renderer/line_bucket.cpp5
-rw-r--r--src/mbgl/renderer/painter.cpp66
-rw-r--r--src/mbgl/renderer/raster_bucket.cpp3
-rw-r--r--src/mbgl/renderer/symbol_bucket.cpp11
13 files changed, 111 insertions, 21 deletions
diff --git a/src/mbgl/geometry/buffer.hpp b/src/mbgl/geometry/buffer.hpp
index 01af5df18e..7e3ced4424 100644
--- a/src/mbgl/geometry/buffer.hpp
+++ b/src/mbgl/geometry/buffer.hpp
@@ -67,6 +67,7 @@ public:
return buffer;
}
+ // Uploads the buffer to the GPU to be available when we need it.
inline void upload() {
if (!buffer) {
bind();
diff --git a/src/mbgl/geometry/glyph_atlas.cpp b/src/mbgl/geometry/glyph_atlas.cpp
index f690004b52..378664f303 100644
--- a/src/mbgl/geometry/glyph_atlas.cpp
+++ b/src/mbgl/geometry/glyph_atlas.cpp
@@ -140,6 +140,12 @@ void GlyphAtlas::removeGlyphs(uintptr_t tileUID) {
}
}
+void GlyphAtlas::upload() {
+ if (dirty) {
+ bind();
+ }
+}
+
void GlyphAtlas::bind() {
if (!texture) {
MBGL_CHECK_ERROR(glGenTextures(1, &texture));
diff --git a/src/mbgl/geometry/glyph_atlas.hpp b/src/mbgl/geometry/glyph_atlas.hpp
index a25c735a8e..dfa568f0fd 100644
--- a/src/mbgl/geometry/glyph_atlas.hpp
+++ b/src/mbgl/geometry/glyph_atlas.hpp
@@ -24,8 +24,13 @@ public:
GlyphPositions&);
void removeGlyphs(uintptr_t tileUID);
+ // Binds the atlas texture to the GPU, and uploads data if it is out of date.
void bind();
+ // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
+ // the texture is only bound when the data is out of date (=dirty).
+ void upload();
+
const uint16_t width = 0;
const uint16_t height = 0;
diff --git a/src/mbgl/geometry/line_atlas.cpp b/src/mbgl/geometry/line_atlas.cpp
index f64989d661..0dcaab2c45 100644
--- a/src/mbgl/geometry/line_atlas.cpp
+++ b/src/mbgl/geometry/line_atlas.cpp
@@ -129,6 +129,12 @@ LinePatternPos LineAtlas::addDash(const std::vector<float> &dasharray, bool roun
return position;
};
+void LineAtlas::upload() {
+ if (dirty) {
+ bind();
+ }
+}
+
void LineAtlas::bind() {
std::lock_guard<std::recursive_mutex> lock(mtx);
diff --git a/src/mbgl/geometry/line_atlas.hpp b/src/mbgl/geometry/line_atlas.hpp
index df60a2dec5..3683272f98 100644
--- a/src/mbgl/geometry/line_atlas.hpp
+++ b/src/mbgl/geometry/line_atlas.hpp
@@ -19,8 +19,13 @@ public:
LineAtlas(uint16_t width, uint16_t height);
~LineAtlas();
+ // Binds the atlas texture to the GPU, and uploads data if it is out of date.
void bind();
+ // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
+ // the texture is only bound when the data is out of date (=dirty).
+ void upload();
+
LinePatternPos getDashPosition(const std::vector<float>&, bool);
LinePatternPos addDash(const std::vector<float> &dasharray, bool round);
diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp
index c2686e2f34..a6156febbc 100644
--- a/src/mbgl/geometry/sprite_atlas.cpp
+++ b/src/mbgl/geometry/sprite_atlas.cpp
@@ -230,6 +230,12 @@ void SpriteAtlas::setSprite(util::ptr<Sprite> sprite_) {
});
}
+void SpriteAtlas::upload() {
+ if (dirty) {
+ bind();
+ }
+}
+
void SpriteAtlas::bind(bool linear) {
bool first = false;
if (!texture) {
diff --git a/src/mbgl/geometry/sprite_atlas.hpp b/src/mbgl/geometry/sprite_atlas.hpp
index 079c15cefd..5605143104 100644
--- a/src/mbgl/geometry/sprite_atlas.hpp
+++ b/src/mbgl/geometry/sprite_atlas.hpp
@@ -50,10 +50,13 @@ public:
SpriteAtlasPosition getPosition(const std::string& name, bool repeating = false);
- // Binds the image buffer of this sprite atlas to the GPU, and uploads data if it is out
- // of date.
+ // Binds the atlas texture to the GPU, and uploads data if it is out of date.
void bind(bool linear = false);
+ // Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
+ // the texture is only bound when the data is out of date (=dirty).
+ void upload();
+
inline float getWidth() const { return width; }
inline float getHeight() const { return height; }
inline float getTextureWidth() const { return width * pixelRatio; }
diff --git a/src/mbgl/renderer/debug_bucket.cpp b/src/mbgl/renderer/debug_bucket.cpp
index 7e367a95b5..edd2165716 100644
--- a/src/mbgl/renderer/debug_bucket.cpp
+++ b/src/mbgl/renderer/debug_bucket.cpp
@@ -17,6 +17,9 @@ DebugBucket::DebugBucket(DebugFontBuffer& fontBuffer_)
}
void DebugBucket::prepare() {
+ fontBuffer.upload();
+
+ renderPass = RenderPass::Translucent;
}
void DebugBucket::render(Painter& painter, const StyleLayer&, const TileID&, const mat4& matrix) {
diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/fill_bucket.cpp
index f8e604ee29..39163c863d 100644
--- a/src/mbgl/renderer/fill_bucket.cpp
+++ b/src/mbgl/renderer/fill_bucket.cpp
@@ -201,6 +201,14 @@ void FillBucket::tessellate() {
}
void FillBucket::prepare() {
+ vertexBuffer.upload();
+ triangleElementsBuffer.upload();
+ lineElementsBuffer.upload();
+
+ // From now on, we're going to render during the opaque and translucent pass.
+ renderPass = static_cast<RenderPass>(
+ static_cast<std::underlying_type<RenderPass>::type>(RenderPass::Opaque) |
+ static_cast<std::underlying_type<RenderPass>::type>(RenderPass::Translucent));
}
void FillBucket::render(Painter& painter,
diff --git a/src/mbgl/renderer/line_bucket.cpp b/src/mbgl/renderer/line_bucket.cpp
index 8e3a8ce9c5..8ef5306d8e 100644
--- a/src/mbgl/renderer/line_bucket.cpp
+++ b/src/mbgl/renderer/line_bucket.cpp
@@ -343,6 +343,11 @@ void LineBucket::addCurrentVertex(const Coordinate& currentVertex,
}
void LineBucket::prepare() {
+ vertexBuffer.upload();
+ triangleElementsBuffer.upload();
+
+ // From now on, we're only going to render during the translucent pass.
+ renderPass = RenderPass::Translucent;
}
void LineBucket::render(Painter& painter,
diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp
index 74ad8b477f..06f58f9c46 100644
--- a/src/mbgl/renderer/painter.cpp
+++ b/src/mbgl/renderer/painter.cpp
@@ -10,6 +10,8 @@
#include <mbgl/style/style_bucket.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
+#include <mbgl/geometry/line_atlas.hpp>
+#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/shader/pattern_shader.hpp>
#include <mbgl/shader/plain_shader.hpp>
@@ -23,7 +25,6 @@
#include <mbgl/shader/dot_shader.hpp>
#include <mbgl/shader/gaussian_shader.hpp>
-
#include <mbgl/util/std.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/mat3.hpp>
@@ -210,10 +211,6 @@ void Painter::prepareTile(const Tile& tile) {
void Painter::render(const Style& style, TransformState state_, TimePoint time) {
state = state_;
- clear();
- resize();
- changeMatrix();
-
std::set<Source*> sources;
for (const auto& source : style.sources) {
if (source->enabled) {
@@ -221,6 +218,27 @@ void Painter::render(const Style& style, TransformState state_, TimePoint time)
}
}
+ // - PREPARATION PASS --------------------------------------------------------------------------
+ // Uploads all required buffers and images before we do any actual rendering.
+
+ // Figure out what buckets we have to draw and what order we have to draw them in.
+ const auto order = determineRenderOrder(style);
+
+ tileStencilBuffer.upload();
+ tileBorderBuffer.upload();
+ spriteAtlas.upload();
+ lineAtlas.upload();
+ glyphAtlas.upload();
+
+ for (const auto& item : order) {
+ if (item.bucket && item.bucket->hasRenderPass(RenderPass::Prepare)) {
+ item.bucket->prepare();
+ }
+ }
+
+ // - CLIPPING MASKS ----------------------------------------------------------------------------
+ // Draws the clipping masks to the stencil buffer.
+
// Update all clipping IDs.
ClipIDGenerator generator;
for (const auto& source : sources) {
@@ -228,6 +246,10 @@ void Painter::render(const Style& style, TransformState state_, TimePoint time)
source->updateMatrices(projMatrix, state);
}
+ clear();
+ resize();
+ changeMatrix();
+
drawClippingMasks(sources);
frameHistory.record(time, state.getNormalizedZoom());
@@ -235,15 +257,12 @@ void Painter::render(const Style& style, TransformState state_, TimePoint time)
// Actually render the layers
if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; }
- // Figure out what buckets we have to draw and what order we have to draw them in.
- const auto order = determineRenderOrder(style);
// TODO: Correctly compute the number of layers recursively beforehand.
const float strata_thickness = 1.0f / (order.size() + 1);
- // - FIRST PASS ------------------------------------------------------------
- // Render everything top-to-bottom by using reverse iterators. Render opaque
- // objects first.
+ // - OPAQUE PASS -------------------------------------------------------------------------------
+ // Render everything top-to-bottom by using reverse iterators. Render opaque objects first.
if (debug::renderTree) {
Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "OPAQUE {");
@@ -253,9 +272,11 @@ void Painter::render(const Style& style, TransformState state_, TimePoint time)
for (auto it = order.rbegin(), end = order.rend(); it != end; ++it, ++i) {
const auto& item = *it;
if (item.bucket && item.tile) {
- setStrata(i * strata_thickness);
- prepareTile(*item.tile);
- item.bucket->render(*this, item.layer, item.tile->id, item.tile->matrix);
+ if (item.bucket->hasRenderPass(RenderPass::Opaque)) {
+ setStrata(i * strata_thickness);
+ prepareTile(*item.tile);
+ item.bucket->render(*this, item.layer, item.tile->id, item.tile->matrix);
+ }
} else {
renderBackground(item.layer);
}
@@ -264,9 +285,9 @@ void Painter::render(const Style& style, TransformState state_, TimePoint time)
Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}");
}
- // - SECOND PASS -----------------------------------------------------------
- // Make a second pass, rendering translucent objects. This time, we render
- // bottom-to-top.
+ // - TRANSLUCENT PASS --------------------------------------------------------------------------
+ // Make a second pass, rendering translucent objects. This time, we render bottom-to-top.
+
if (debug::renderTree) {
Log::Info(Event::Render, "%*s%s", indent++ * 4, "", "TRANSLUCENT {");
}
@@ -275,9 +296,11 @@ void Painter::render(const Style& style, TransformState state_, TimePoint time)
for (auto it = order.begin(), end = order.end(); it != end; ++it, --i) {
const auto& item = *it;
if (item.bucket && item.tile) {
- setStrata(i * strata_thickness);
- prepareTile(*item.tile);
- item.bucket->render(*this, item.layer, item.tile->id, item.tile->matrix);
+ if (item.bucket->hasRenderPass(RenderPass::Translucent)) {
+ setStrata(i * strata_thickness);
+ prepareTile(*item.tile);
+ item.bucket->render(*this, item.layer, item.tile->id, item.tile->matrix);
+ }
}
}
if (debug::renderTree) {
@@ -293,6 +316,11 @@ void Painter::render(const Style& style, TransformState state_, TimePoint time)
for (const auto& source : sources) {
source->finishRender(*this);
}
+
+ // TODO: Find a better way to unbind VAOs after we're done with them without introducing
+ // unnecessary bind(0)/bind(N) sequences.
+ MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, 0));
+ MBGL_CHECK_ERROR(gl::BindVertexArray(0));
}
std::vector<RenderItem> Painter::determineRenderOrder(const Style& style) {
diff --git a/src/mbgl/renderer/raster_bucket.cpp b/src/mbgl/renderer/raster_bucket.cpp
index 7f1f92b5ca..946d817498 100644
--- a/src/mbgl/renderer/raster_bucket.cpp
+++ b/src/mbgl/renderer/raster_bucket.cpp
@@ -14,6 +14,9 @@ RasterBucket::RasterBucket(TexturePool& texturePool, const StyleLayoutRaster& la
}
void RasterBucket::prepare() {
+ if (hasData()) {
+ raster.upload();
+ }
}
void RasterBucket::render(Painter& painter,
diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp
index d520e4de2e..62ede28401 100644
--- a/src/mbgl/renderer/symbol_bucket.cpp
+++ b/src/mbgl/renderer/symbol_bucket.cpp
@@ -37,6 +37,17 @@ SymbolBucket::~SymbolBucket() {
}
void SymbolBucket::prepare() {
+ if (hasTextData()) {
+ text.vertices.upload();
+ text.triangles.upload();
+ }
+ if (hasIconData()) {
+ icon.vertices.upload();
+ icon.triangles.upload();
+ }
+
+ // From now on, we're going to render during the opaque and translucent pass.
+ renderPass = RenderPass::Translucent;
}
void SymbolBucket::render(Painter& painter,