diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2015-04-23 11:39:12 +0200 |
---|---|---|
committer | Konstantin Käfer <mail@kkaefer.com> | 2015-05-04 17:33:05 +0200 |
commit | 97a22b6f5fea5666025189231f5fecc4946ceb0b (patch) | |
tree | 7aae1422c631d92c01c4deb91b7d128a64f2df2c /src/mbgl | |
parent | 28b9b3f9d64207a03b6125b3e299efda59952a03 (diff) | |
download | qtlocation-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/mbgl')
-rw-r--r-- | src/mbgl/geometry/buffer.hpp | 1 | ||||
-rw-r--r-- | src/mbgl/geometry/glyph_atlas.cpp | 6 | ||||
-rw-r--r-- | src/mbgl/geometry/glyph_atlas.hpp | 5 | ||||
-rw-r--r-- | src/mbgl/geometry/line_atlas.cpp | 6 | ||||
-rw-r--r-- | src/mbgl/geometry/line_atlas.hpp | 5 | ||||
-rw-r--r-- | src/mbgl/geometry/sprite_atlas.cpp | 6 | ||||
-rw-r--r-- | src/mbgl/geometry/sprite_atlas.hpp | 7 | ||||
-rw-r--r-- | src/mbgl/renderer/debug_bucket.cpp | 3 | ||||
-rw-r--r-- | src/mbgl/renderer/fill_bucket.cpp | 8 | ||||
-rw-r--r-- | src/mbgl/renderer/line_bucket.cpp | 5 | ||||
-rw-r--r-- | src/mbgl/renderer/painter.cpp | 66 | ||||
-rw-r--r-- | src/mbgl/renderer/raster_bucket.cpp | 3 | ||||
-rw-r--r-- | src/mbgl/renderer/symbol_bucket.cpp | 11 |
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, |