diff --git a/src/mbgl/geometry/buffer.hpp b/src/mbgl/geometry/buffer.hpp index 01af5df18..7e3ced442 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 f690004b5..378664f30 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 a25c735a8..dfa568f0f 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 f64989d66..0dcaab2c4 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 &dasharray, bool roun return position; }; +void LineAtlas::upload() { + if (dirty) { + bind(); + } +} + void LineAtlas::bind() { std::lock_guard lock(mtx); diff --git a/src/mbgl/geometry/line_atlas.hpp b/src/mbgl/geometry/line_atlas.hpp index df60a2dec..3683272f9 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&, bool); LinePatternPos addDash(const std::vector &dasharray, bool round); diff --git a/src/mbgl/geometry/sprite_atlas.cpp b/src/mbgl/geometry/sprite_atlas.cpp index c2686e2f3..a6156febb 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_) { }); } +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 079c15cef..560514310 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 7e367a95b..edd216571 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 f8e604ee2..39163c863 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( + static_cast::type>(RenderPass::Opaque) | + static_cast::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 8e3a8ce9c..8ef5306d8 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 74ad8b477..06f58f9c4 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -10,6 +10,8 @@ #include #include +#include +#include #include #include @@ -23,7 +25,6 @@ #include #include - #include #include #include @@ -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 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 Painter::determineRenderOrder(const Style& style) { diff --git a/src/mbgl/renderer/raster_bucket.cpp b/src/mbgl/renderer/raster_bucket.cpp index 7f1f92b5c..946d81749 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 d520e4de2..62ede2840 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,