#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { using namespace style; Painter::Painter(gl::Context& context_, const TransformState& state_) : context(context_), state(state_), tileTriangleVertexBuffer(context.createVertexBuffer(std::vector {{ { 0, 0 }, { util::EXTENT, 0 }, { 0, util::EXTENT }, { util::EXTENT, 0 }, { 0, util::EXTENT }, { util::EXTENT, util::EXTENT } }})), tileLineStripVertexBuffer(context.createVertexBuffer(std::vector {{ { 0, 0 }, { util::EXTENT, 0 }, { util::EXTENT, util::EXTENT }, { 0, util::EXTENT }, { 0, 0 } }})), rasterVertexBuffer(context.createVertexBuffer(std::vector {{ { 0, 0, 0, 0 }, { util::EXTENT, 0, 32767, 0 }, { 0, util::EXTENT, 0, 32767 }, { util::EXTENT, util::EXTENT, 32767, 32767 } }})) { #ifndef NDEBUG gl::debugging::enable(); #endif shaders = std::make_unique(context); #ifndef NDEBUG overdrawShaders = std::make_unique(context, gl::Shader::Overdraw); #endif } Painter::~Painter() = default; bool Painter::needsAnimation() const { return frameHistory.needsAnimation(util::DEFAULT_FADE_DURATION); } void Painter::setClipping(const ClipID& clip) { const GLint ref = (GLint)clip.reference.to_ulong(); const GLuint mask = (GLuint)clip.mask.to_ulong(); context.stencilFunc = { gl::StencilTestFunction::Equal, ref, mask }; } void Painter::cleanup() { context.performCleanup(); } void Painter::render(const Style& style, const FrameData& frame_, View& view, SpriteAtlas& annotationSpriteAtlas) { frame = frame_; if (frame.contextMode == GLContextMode::Shared) { context.setDirtyState(); } PaintParameters parameters { #ifndef NDEBUG paintMode() == PaintMode::Overdraw ? *overdrawShaders : *shaders, #else *shaders, #endif view }; glyphAtlas = style.glyphAtlas.get(); spriteAtlas = style.spriteAtlas.get(); lineAtlas = style.lineAtlas.get(); RenderData renderData = style.getRenderData(frame.debugOptions); const std::vector& order = renderData.order; const std::unordered_set& sources = renderData.sources; const Color& background = renderData.backgroundColor; // Update the default matrices to the current viewport dimensions. state.getProjMatrix(projMatrix); pixelsToGLUnits = {{ 2.0f / state.getSize().width, -2.0f / state.getSize().height }}; if (state.getViewportMode() == ViewportMode::FlippedY) { pixelsToGLUnits[1] *= -1; } frameHistory.record(frame.timePoint, state.getZoom(), frame.mapMode == MapMode::Continuous ? util::DEFAULT_FADE_DURATION : Milliseconds(0)); // - UPLOAD PASS ------------------------------------------------------------------------------- // Uploads all required buffers and images before we do any actual rendering. { MBGL_DEBUG_GROUP("upload"); spriteAtlas->upload(context, 0); lineAtlas->upload(context, 0); glyphAtlas->upload(context, 0); frameHistory.upload(context, 0); annotationSpriteAtlas.upload(context, 0); for (const auto& item : order) { if (item.bucket && item.bucket->needsUpload()) { item.bucket->upload(context); } } } // - CLEAR ------------------------------------------------------------------------------------- // Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any // tiles whatsoever. { MBGL_DEBUG_GROUP("clear"); view.bind(); context.stencilFunc = { gl::StencilTestFunction::Always, 0, ~0u }; context.stencilTest = true; context.stencilMask = 0xFF; context.depthTest = false; context.depthMask = true; context.colorMask = { true, true, true, true }; if (paintMode() == PaintMode::Overdraw) { context.blend = true; context.blendFunc = { gl::BlendSourceFactor::ConstantColor, gl::BlendDestinationFactor::One }; const float overdraw = 1.0f / 8.0f; context.blendColor = { overdraw, overdraw, overdraw, 0.0f }; context.clearColor = Color::black(); } else { context.clearColor = background; } context.clearStencil = 0; context.clearDepth = 1; MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); } // - CLIPPING MASKS ---------------------------------------------------------------------------- // Draws the clipping masks to the stencil buffer. { MBGL_DEBUG_GROUP("clip"); // Update all clipping IDs. algorithm::ClipIDGenerator generator; for (const auto& source : sources) { source->baseImpl->startRender(generator, projMatrix, state); } drawClippingMasks(parameters, generator.getStencils()); } #if not MBGL_USE_GLES2 and not defined(NDEBUG) if (frame.debugOptions & MapDebugOptions::StencilClip) { renderClipMasks(parameters); return; } #endif // Actually render the layers if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; } // TODO: Correctly compute the number of layers recursively beforehand. depthRangeSize = 1 - (order.size() + 2) * numSublayers * depthEpsilon; // - OPAQUE PASS ------------------------------------------------------------------------------- // Render everything top-to-bottom by using reverse iterators. Render opaque objects first. renderPass(parameters, RenderPass::Opaque, order.rbegin(), order.rend(), 0, 1); // - TRANSLUCENT PASS -------------------------------------------------------------------------- // Make a second pass, rendering translucent objects. This time, we render bottom-to-top. renderPass(parameters, RenderPass::Translucent, order.begin(), order.end(), static_cast(order.size()) - 1, -1); if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; } // - DEBUG PASS -------------------------------------------------------------------------------- // Renders debug overlays. { MBGL_DEBUG_GROUP("debug"); // Finalize the rendering, e.g. by calling debug render calls per tile. // This guarantees that we have at least one function per tile called. // When only rendering layers via the stylesheet, it's possible that we don't // ever visit a tile during rendering. for (const auto& source : sources) { source->baseImpl->finishRender(*this); } } #if not MBGL_USE_GLES2 and not defined(NDEBUG) if (frame.debugOptions & MapDebugOptions::DepthBuffer) { renderDepthBuffer(parameters); } #endif // TODO: Find a better way to unbind VAOs after we're done with them without introducing // unnecessary bind(0)/bind(N) sequences. { MBGL_DEBUG_GROUP("cleanup"); context.activeTexture = 1; context.texture[1] = 0; context.activeTexture = 0; context.texture[0] = 0; context.vertexArrayObject = 0; } } template void Painter::renderPass(PaintParameters& parameters, RenderPass pass_, Iterator it, Iterator end, uint32_t i, int8_t increment) { pass = pass_; MBGL_DEBUG_GROUP(pass == RenderPass::Opaque ? "opaque" : "translucent"); if (debug::renderTree) { Log::Info(Event::Render, "%*s%s {", indent++ * 4, "", pass == RenderPass::Opaque ? "opaque" : "translucent"); } for (; it != end; ++it, i += increment) { currentLayer = i; const auto& item = *it; const Layer& layer = item.layer; if (!layer.baseImpl->hasRenderPass(pass)) continue; if (paintMode() == PaintMode::Overdraw) { context.blend = true; } else if (pass == RenderPass::Translucent) { context.blend = true; context.blendFunc = { gl::BlendSourceFactor::One, gl::BlendDestinationFactor::OneMinusSrcAlpha }; } else { context.blend = false; } context.colorMask = { true, true, true, true }; context.stencilMask = 0x0; if (layer.is()) { MBGL_DEBUG_GROUP("background"); renderBackground(parameters, *layer.as()); } else if (layer.is()) { MBGL_DEBUG_GROUP(layer.baseImpl->id + " - custom"); // Reset GL state to a known state so the CustomLayer always has a clean slate. context.vertexArrayObject = 0; context.depthFunc = gl::DepthTestFunction::LessEqual; context.depthTest = true; context.depthMask = false; context.stencilTest = false; setDepthSublayer(0); layer.as()->impl->render(state); // Reset the view back to our original one, just in case the CustomLayer changed // the viewport or Framebuffer. parameters.view.bind(); context.setDirtyState(); } else { MBGL_DEBUG_GROUP(layer.baseImpl->id + " - " + util::toString(item.tile->id)); if (item.bucket->needsClipping()) { setClipping(item.tile->clip); } item.bucket->render(*this, parameters, layer, *item.tile); } } if (debug::renderTree) { Log::Info(Event::Render, "%*s%s", --indent * 4, "", "}"); } } void Painter::setDepthSublayer(int n) { float nearDepth = ((1 + currentLayer) * numSublayers + n) * depthEpsilon; float farDepth = nearDepth + depthRangeSize; context.depthRange = { nearDepth, farDepth }; } } // namespace mbgl