From 4a47413a7175fc69cd823f94356c939c3aa1d542 Mon Sep 17 00:00:00 2001 From: Daria Volvenkova Date: Tue, 19 Dec 2017 14:50:17 +0300 Subject: [PATCH] Transit scheme. --- drape_frontend/CMakeLists.txt | 8 + drape_frontend/backend_renderer.cpp | 68 ++ drape_frontend/backend_renderer.hpp | 7 + drape_frontend/drape_engine.cpp | 23 + drape_frontend/drape_engine.hpp | 7 + drape_frontend/frontend_renderer.cpp | 114 ++- drape_frontend/frontend_renderer.hpp | 6 +- drape_frontend/message.hpp | 8 + drape_frontend/message_subclasses.hpp | 67 ++ drape_frontend/render_state.hpp | 1 + drape_frontend/shaders/shader_index.txt | 2 + drape_frontend/shaders/shaders_lib.glsl | 2 +- drape_frontend/shaders/transit.fsh.glsl | 10 + drape_frontend/shaders/transit.vsh.glsl | 26 + .../shaders/transit_marker.fsh.glsl | 22 + .../shaders/transit_marker.vsh.glsl | 26 + drape_frontend/text_layout.cpp | 7 +- drape_frontend/text_layout.hpp | 2 + drape_frontend/traffic_generator.cpp | 2 +- drape_frontend/traffic_renderer.cpp | 4 +- drape_frontend/traffic_renderer.hpp | 2 +- drape_frontend/transit_scheme_builder.cpp | 807 ++++++++++++++++++ drape_frontend/transit_scheme_builder.hpp | 167 ++++ drape_frontend/transit_scheme_renderer.cpp | 247 ++++++ drape_frontend/transit_scheme_renderer.hpp | 59 ++ map/framework.cpp | 50 +- map/framework.hpp | 7 + map/routing_manager.cpp | 8 +- map/routing_manager.hpp | 12 +- map/transit/transit_reader.cpp | 165 +++- map/transit/transit_reader.hpp | 80 +- transit/CMakeLists.txt | 1 + transit/transit_display_info.hpp | 36 + .../drape_frontend.xcodeproj/project.pbxproj | 16 + 34 files changed, 1984 insertions(+), 85 deletions(-) create mode 100644 drape_frontend/shaders/transit.fsh.glsl create mode 100644 drape_frontend/shaders/transit.vsh.glsl create mode 100644 drape_frontend/shaders/transit_marker.fsh.glsl create mode 100644 drape_frontend/shaders/transit_marker.vsh.glsl create mode 100644 drape_frontend/transit_scheme_builder.cpp create mode 100644 drape_frontend/transit_scheme_builder.hpp create mode 100644 drape_frontend/transit_scheme_renderer.cpp create mode 100644 drape_frontend/transit_scheme_renderer.hpp create mode 100644 transit/transit_display_info.hpp diff --git a/drape_frontend/CMakeLists.txt b/drape_frontend/CMakeLists.txt index 2dfcaf5d4b..06458309bc 100644 --- a/drape_frontend/CMakeLists.txt +++ b/drape_frontend/CMakeLists.txt @@ -208,6 +208,10 @@ set( traffic_generator.hpp traffic_renderer.cpp traffic_renderer.hpp + transit_scheme_builder.cpp + transit_scheme_builder.hpp + transit_scheme_renderer.cpp + transit_scheme_renderer.hpp user_event_stream.cpp user_event_stream.hpp user_mark_generator.cpp @@ -287,6 +291,10 @@ set( shaders/traffic.vsh.glsl shaders/traffic_line.fsh.glsl shaders/traffic_line.vsh.glsl + shaders/transit_marker.fsh.glsl + shaders/transit_marker.vsh.glsl + shaders/transit.fsh.glsl + shaders/transit.vsh.glsl shaders/user_mark.fsh.glsl shaders/user_mark.vsh.glsl shaders/user_mark_billboard.vsh.glsl diff --git a/drape_frontend/backend_renderer.cpp b/drape_frontend/backend_renderer.cpp index bd775275ed..5cf2be8d96 100644 --- a/drape_frontend/backend_renderer.cpp +++ b/drape_frontend/backend_renderer.cpp @@ -32,6 +32,10 @@ BackendRenderer::BackendRenderer(Params && params) , m_readManager(make_unique_dp(params.m_commutator, m_model, params.m_allow3dBuildings, params.m_trafficEnabled, std::move(params.m_isUGCFn))) + , m_transitBuilder(make_unique_dp(bind(&BackendRenderer::FlushTransitRenderData, this, _1), + bind(&BackendRenderer::FlushTransitMarkersRenderData, this, _1), + bind(&BackendRenderer::FlushTransitTextRenderData, this, _1), + bind(&BackendRenderer::FlushTransitStubsRenderData, this, _1))) , m_trafficGenerator(make_unique_dp(bind(&BackendRenderer::FlushTrafficRenderData, this, _1))) , m_userMarkGenerator(make_unique_dp(bind(&BackendRenderer::FlushUserMarksRenderData, this, _1))) , m_requestedTiles(params.m_requestedTiles) @@ -335,6 +339,7 @@ void BackendRenderer::AcceptMessage(ref_ptr message) m_texMng->OnSwitchMapStyle(); RecacheMapShapes(); m_trafficGenerator->InvalidateTexturesCache(); + m_transitBuilder->BuildScheme(m_texMng); break; } @@ -431,6 +436,41 @@ void BackendRenderer::AcceptMessage(ref_ptr message) break; } + case Message::UpdateTransitScheme: + { + ref_ptr msg = message; + m_transitBuilder->SetVisibleMwms(msg->GetVisibleMwms()); + m_transitBuilder->UpdateScheme(msg->GetTransitDisplayInfos()); + m_transitBuilder->BuildScheme(m_texMng); + break; + } + + case Message::RegenerateTransitScheme: + { + m_transitBuilder->BuildScheme(m_texMng); + break; + } + + case Message::ClearTransitSchemeData: + { + ref_ptr msg = message; + m_transitBuilder->Clear(msg->GetMwmId()); + m_commutator->PostMessage(ThreadsCommutator::RenderThread, + make_unique_dp(msg->GetMwmId()), + MessagePriority::Normal); + break; + } + + case Message::EnableTransitScheme: + { + ref_ptr msg = message; + if (!msg->Enable()) + m_transitBuilder->Clear(); + m_commutator->PostMessage(ThreadsCommutator::RenderThread, + make_unique_dp(msg->Enable()), + MessagePriority::Normal); + break; + } case Message::DrapeApiAddLines: { ref_ptr msg = message; @@ -625,6 +665,34 @@ void BackendRenderer::FlushGeometry(TileKey const & key, dp::GLState const & sta MessagePriority::Normal); } +void BackendRenderer::FlushTransitRenderData(TransitRenderData && renderData) +{ + m_commutator->PostMessage(ThreadsCommutator::RenderThread, + make_unique_dp(move(renderData)), + MessagePriority::Normal); +} + +void BackendRenderer::FlushTransitMarkersRenderData(TransitRenderData && renderData) +{ + m_commutator->PostMessage(ThreadsCommutator::RenderThread, + make_unique_dp(move(renderData)), + MessagePriority::Normal); +} + +void BackendRenderer::FlushTransitTextRenderData(TransitRenderData && renderData) +{ + m_commutator->PostMessage(ThreadsCommutator::RenderThread, + make_unique_dp(move(renderData)), + MessagePriority::Normal); +} + +void BackendRenderer::FlushTransitStubsRenderData(TransitRenderData && renderData) +{ + m_commutator->PostMessage(ThreadsCommutator::RenderThread, + make_unique_dp(move(renderData)), + MessagePriority::Normal); +} + void BackendRenderer::FlushTrafficRenderData(TrafficRenderData && renderData) { m_commutator->PostMessage(ThreadsCommutator::RenderThread, diff --git a/drape_frontend/backend_renderer.hpp b/drape_frontend/backend_renderer.hpp index 3f945f9aec..40bdaf771f 100644 --- a/drape_frontend/backend_renderer.hpp +++ b/drape_frontend/backend_renderer.hpp @@ -9,6 +9,7 @@ #include "drape_frontend/overlay_batcher.hpp" #include "drape_frontend/requested_tiles.hpp" #include "drape_frontend/traffic_generator.hpp" +#include "drape_frontend/transit_scheme_builder.hpp" #include "drape_frontend/user_mark_generator.hpp" #include "drape/pointers.hpp" @@ -99,6 +100,11 @@ private: void InitGLDependentResource(); void FlushGeometry(TileKey const & key, dp::GLState const & state, drape_ptr && buffer); + void FlushTransitRenderData(TransitRenderData && renderData); + void FlushTransitMarkersRenderData(TransitRenderData && renderData); + void FlushTransitTextRenderData(TransitRenderData && renderData); + void FlushTransitStubsRenderData(TransitRenderData && renderData); + void FlushTrafficRenderData(TrafficRenderData && renderData); void FlushUserMarksRenderData(TUserMarksRenderData && renderData); @@ -108,6 +114,7 @@ private: drape_ptr> m_batchersPool; drape_ptr m_readManager; drape_ptr m_routeBuilder; + drape_ptr m_transitBuilder; drape_ptr m_trafficGenerator; drape_ptr m_userMarkGenerator; drape_ptr m_drapeApiBuilder; diff --git a/drape_frontend/drape_engine.cpp b/drape_frontend/drape_engine.cpp index 4fee4b813f..98bc54e27f 100644 --- a/drape_frontend/drape_engine.cpp +++ b/drape_frontend/drape_engine.cpp @@ -735,6 +735,29 @@ void DrapeEngine::SetSimplifiedTrafficColors(bool simplified) MessagePriority::Normal); } +void DrapeEngine::EnableTransitScheme(bool enable) +{ + m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread, + make_unique_dp(enable), + MessagePriority::Normal); +} + +void DrapeEngine::ClearTransitSchemeCache(MwmSet::MwmId const & mwmId) +{ + m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread, + make_unique_dp(mwmId), + MessagePriority::Normal); +} + +void DrapeEngine::UpdateTransitScheme(TransitDisplayInfos && transitDisplayInfos, + std::vector const & visibleMwms) +{ + m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread, + make_unique_dp(std::move(transitDisplayInfos), + visibleMwms), + MessagePriority::Normal); +} + void DrapeEngine::SetFontScaleFactor(double scaleFactor) { double const kMinScaleFactor = 0.5; diff --git a/drape_frontend/drape_engine.hpp b/drape_frontend/drape_engine.hpp index 33b5e7a081..b2f5c1844f 100644 --- a/drape_frontend/drape_engine.hpp +++ b/drape_frontend/drape_engine.hpp @@ -19,6 +19,8 @@ #include "traffic/traffic_info.hpp" +#include "transit/transit_display_info.hpp" + #include "platform/location.hpp" #include "geometry/polyline2d.hpp" @@ -200,6 +202,11 @@ public: void ClearTrafficCache(MwmSet::MwmId const & mwmId); void SetSimplifiedTrafficColors(bool simplified); + void EnableTransitScheme(bool enable); + void UpdateTransitScheme(TransitDisplayInfos && transitDisplayInfos, + std::vector const & visibleMwms); + void ClearTransitSchemeCache(MwmSet::MwmId const & mwmId); + void SetFontScaleFactor(double scaleFactor); void RunScenario(ScenarioManager::ScenarioData && scenarioData, diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp index f5053eaf73..e9c9507a54 100755 --- a/drape_frontend/frontend_renderer.cpp +++ b/drape_frontend/frontend_renderer.cpp @@ -121,6 +121,7 @@ FrontendRenderer::FrontendRenderer(Params && params) : BaseRenderer(ThreadsCommutator::RenderThread, params) , m_gpuProgramManager(new dp::GpuProgramManager()) , m_trafficRenderer(new TrafficRenderer()) + , m_transitSchemeRenderer(new TransitSchemeRenderer()) , m_drapeApiRenderer(new DrapeApiRenderer()) , m_overlayTree(new dp::OverlayTree(VisualParams::Instance().GetVisualScale())) , m_enablePerspectiveInNavigation(false) @@ -461,6 +462,38 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) break; } + case Message::FlushTransitScheme: + { + ref_ptr msg = message; + auto renderData = msg->AcceptRenderData(); + m_transitSchemeRenderer->AddRenderData(make_ref(m_gpuProgramManager), std::move(renderData)); + break; + } + + case Message::FlushTransitMarkers: + { + ref_ptr msg = message; + auto renderData = msg->AcceptRenderData(); + m_transitSchemeRenderer->AddMarkersRenderData(make_ref(m_gpuProgramManager), std::move(renderData)); + break; + } + + case Message::FlushTransitText: + { + ref_ptr msg = message; + auto renderData = msg->AcceptRenderData(); + m_transitSchemeRenderer->AddTextRenderData(make_ref(m_gpuProgramManager), std::move(renderData)); + break; + } + + case Message::FlushTransitStubs: + { + ref_ptr msg = message; + auto renderData = msg->AcceptRenderData(); + m_transitSchemeRenderer->AddStubsRenderData(make_ref(m_gpuProgramManager), std::move(renderData)); + break; + } + case Message::FlushSubrouteArrows: { ref_ptr msg = message; @@ -740,6 +773,21 @@ void FrontendRenderer::AcceptMessage(ref_ptr message) break; } + case Message::EnableTransitScheme: + { + ref_ptr msg = message; + if (!msg->Enable()) + m_transitSchemeRenderer->ClearGLDependentResources(); + break; + } + + case Message::ClearTransitSchemeData: + { + ref_ptr msg = message; + m_transitSchemeRenderer->Clear(msg->GetMwmId()); + break; + } + case Message::EnableTraffic: { ref_ptr msg = message; @@ -885,6 +933,9 @@ void FrontendRenderer::UpdateGLResources() m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, make_unique_dp(), MessagePriority::UberHighSingleton); + m_commutator->PostMessage(ThreadsCommutator::ResourceUploadThread, + make_unique_dp(), + MessagePriority::Normal); } m_gpsTrackRenderer->Update(); @@ -1173,7 +1224,7 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) if (m_buildingsFramebuffer->IsSupported()) { RenderTrafficLayer(modelView); - if (!HasTransitData()) + if (!HasTransitRouteData()) RenderRouteLayer(modelView); Render3dLayer(modelView, true /* useFramebuffer */); } @@ -1181,7 +1232,7 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) { Render3dLayer(modelView, false /* useFramebuffer */); RenderTrafficLayer(modelView); - if (!HasTransitData()) + if (!HasTransitRouteData()) RenderRouteLayer(modelView); } @@ -1221,7 +1272,7 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) m_generalUniforms); } - if (HasTransitData()) + if (HasTransitRouteData()) RenderRouteLayer(modelView); { @@ -1232,6 +1283,9 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) RenderSearchMarksLayer(modelView); } + if (!HasTransitRouteData()) + RenderTransitSchemeLayer(modelView); + m_myPositionController->Render(modelView, m_currentZoomLevel, make_ref(m_gpuProgramManager), m_generalUniforms); @@ -1320,11 +1374,25 @@ void FrontendRenderer::RenderNavigationOverlayLayer(ScreenBase const & modelView } } -bool FrontendRenderer::HasTransitData() +bool FrontendRenderer::HasTransitRouteData() { return m_routeRenderer->HasTransitData(); } +void FrontendRenderer::RenderTransitSchemeLayer(ScreenBase const & modelView) +{ + GLFunctions::glClear(gl_const::GLDepthBit); + GLFunctions::glEnable(gl_const::GLDepthTest); + if (m_transitSchemeRenderer->HasRenderData(m_currentZoomLevel)) + { + RenderTransitBackground(); + GLFunctions::glEnable(gl_const::GLDepthTest); + m_transitSchemeRenderer->RenderTransit(modelView, m_currentZoomLevel, make_ref(m_gpuProgramManager), + make_ref(m_postprocessRenderer), m_generalUniforms); + } + GLFunctions::glDisable(gl_const::GLDepthTest); +} + void FrontendRenderer::RenderTrafficLayer(ScreenBase const & modelView) { GLFunctions::glClear(gl_const::GLDepthBit); @@ -1337,22 +1405,28 @@ void FrontendRenderer::RenderTrafficLayer(ScreenBase const & modelView) GLFunctions::glDisable(gl_const::GLDepthTest); } +void FrontendRenderer::RenderTransitBackground() +{ + if (!m_finishTexturesInitialization) + return; + + GLFunctions::glDisable(gl_const::GLDepthTest); + + dp::TextureManager::ColorRegion region; + m_texMng->GetColorRegion(df::GetColorConstant(kTransitBackgroundColor), region); + if (!m_transitBackground->IsInitialized()) + { + auto prg = m_gpuProgramManager->GetProgram(gpu::SCREEN_QUAD_PROGRAM); + m_transitBackground->SetTextureRect(region.GetTexRect(), prg); + } + m_transitBackground->RenderTexture(make_ref(m_gpuProgramManager), + static_cast(region.GetTexture()->GetID()), 1.0f); +} + void FrontendRenderer::RenderRouteLayer(ScreenBase const & modelView) { - if (m_finishTexturesInitialization && HasTransitData()) - { - GLFunctions::glDisable(gl_const::GLDepthTest); - - dp::TextureManager::ColorRegion region; - m_texMng->GetColorRegion(df::GetColorConstant(kTransitBackgroundColor), region); - if (!m_transitBackground->IsInitialized()) - { - auto prg = m_gpuProgramManager->GetProgram(gpu::SCREEN_QUAD_PROGRAM); - m_transitBackground->SetTextureRect(region.GetTexRect(), prg); - } - m_transitBackground->RenderTexture(make_ref(m_gpuProgramManager), - static_cast(region.GetTexture()->GetID()), 1.0f); - } + if (HasTransitRouteData()) + RenderTransitBackground(); GLFunctions::glClear(gl_const::GLDepthBit); GLFunctions::glEnable(gl_const::GLDepthTest); @@ -1412,6 +1486,8 @@ void FrontendRenderer::BuildOverlayTree(ScreenBase const & modelView) for (drape_ptr & group : overlay.m_renderGroups) UpdateOverlayTree(modelView, group); } + if (m_transitSchemeRenderer->HasRenderData(m_currentZoomLevel) && !HasTransitRouteData()) + m_transitSchemeRenderer->CollectOverlays(make_ref(m_overlayTree), modelView); EndUpdateOverlayTree(); } @@ -1831,6 +1907,7 @@ void FrontendRenderer::OnContextDestroy() m_trafficRenderer->ClearGLDependentResources(); m_drapeApiRenderer->Clear(); m_postprocessRenderer->ClearGLDependentResources(); + m_transitSchemeRenderer->ClearGLDependentResources(); m_transitBackground.reset(); @@ -2030,6 +2107,7 @@ void FrontendRenderer::ReleaseResources() m_buildingsFramebuffer.reset(); m_screenQuadRenderer.reset(); m_trafficRenderer.reset(); + m_transitSchemeRenderer.reset(); m_postprocessRenderer.reset(); m_transitBackground.reset(); diff --git a/drape_frontend/frontend_renderer.hpp b/drape_frontend/frontend_renderer.hpp index ec8db4be0e..d8a08dadb2 100755 --- a/drape_frontend/frontend_renderer.hpp +++ b/drape_frontend/frontend_renderer.hpp @@ -19,6 +19,7 @@ #include "drape_frontend/threads_commutator.hpp" #include "drape_frontend/tile_info.hpp" #include "drape_frontend/traffic_renderer.hpp" +#include "drape_frontend/transit_scheme_renderer.hpp" #include "drape_frontend/user_event_stream.hpp" #include "drape/gpu_program_manager.hpp" @@ -162,11 +163,13 @@ private: void RenderNavigationOverlayLayer(ScreenBase const & modelView); void RenderUserMarksLayer(ScreenBase const & modelView, RenderState::DepthLayer layerId, bool enableDepthTest = true); + void RenderTransitSchemeLayer(ScreenBase const & modelView); void RenderTrafficLayer(ScreenBase const & modelView); void RenderRouteLayer(ScreenBase const & modelView); void RenderSearchMarksLayer(ScreenBase const & modelView); + void RenderTransitBackground(); - bool HasTransitData(); + bool HasTransitRouteData(); ScreenBase const & ProcessEvents(bool & modelViewChanged, bool & viewportChanged); void PrepareScene(ScreenBase const & modelView); @@ -256,6 +259,7 @@ private: drape_ptr m_selectionShape; drape_ptr m_routeRenderer; drape_ptr m_trafficRenderer; + drape_ptr m_transitSchemeRenderer; drape_ptr m_buildingsFramebuffer; drape_ptr m_screenQuadRenderer; drape_ptr m_gpsTrackRenderer; diff --git a/drape_frontend/message.hpp b/drape_frontend/message.hpp index 32abe518d7..3d292d6de0 100644 --- a/drape_frontend/message.hpp +++ b/drape_frontend/message.hpp @@ -86,6 +86,14 @@ public: PostUserEvent, FinishTexturesInitialization, EnableUGCRendering, + EnableTransitScheme, + UpdateTransitScheme, + ClearTransitSchemeData, + RegenerateTransitScheme, + FlushTransitScheme, + FlushTransitMarkers, + FlushTransitText, + FlushTransitStubs, }; virtual ~Message() {} diff --git a/drape_frontend/message_subclasses.hpp b/drape_frontend/message_subclasses.hpp index 5c05a47cf1..85cd07be8e 100644 --- a/drape_frontend/message_subclasses.hpp +++ b/drape_frontend/message_subclasses.hpp @@ -17,6 +17,7 @@ #include "drape_frontend/selection_shape.hpp" #include "drape_frontend/tile_utils.hpp" #include "drape_frontend/traffic_generator.hpp" +#include "drape_frontend/transit_scheme_builder.hpp" #include "drape_frontend/user_event_stream.hpp" #include "drape_frontend/user_mark_shapes.hpp" #include "drape_frontend/user_marks_provider.hpp" @@ -1019,6 +1020,72 @@ private: bool const m_isSimplified; }; +class EnableTransitSchemeMessage : public Message +{ +public: + explicit EnableTransitSchemeMessage(bool enable) + : m_enable(enable) + {} + + Type GetType() const override { return Message::EnableTransitScheme; } + + bool Enable() { return m_enable; } + +private: + bool m_enable = false; +}; + +class ClearTransitSchemeDataMessage : public Message +{ +public: + explicit ClearTransitSchemeDataMessage(MwmSet::MwmId const & mwmId) + : m_mwmId(mwmId) + {} + + Type GetType() const override { return Message::ClearTransitSchemeData; } + + MwmSet::MwmId const & GetMwmId() { return m_mwmId; } + +private: + MwmSet::MwmId m_mwmId; +}; + +class UpdateTransitSchemeMessage : public Message +{ +public: + UpdateTransitSchemeMessage(TransitDisplayInfos && transitInfos, + std::vector const & visibleMwms) + : m_transitInfos(move(transitInfos)), m_visibleMwms(visibleMwms) + {} + + Type GetType() const override { return Message::UpdateTransitScheme; } + + TransitDisplayInfos & GetTransitDisplayInfos() { return m_transitInfos; } + std::vector const & GetVisibleMwms() const { return m_visibleMwms; } + +private: + TransitDisplayInfos m_transitInfos; + std::vector m_visibleMwms; +}; + +class RegenerateTransitMessage : public Message +{ +public: + Type GetType() const override { return Message::RegenerateTransitScheme; } +}; + +using FlushTransitSchemeMessage = FlushRenderDataMessage; + +using FlushTransitMarkersMessage = FlushRenderDataMessage; + +using FlushTransitTextMessage = FlushRenderDataMessage; + +using FlushTransitStubsMessage = FlushRenderDataMessage; + class DrapeApiAddLinesMessage : public Message { public: diff --git a/drape_frontend/render_state.hpp b/drape_frontend/render_state.hpp index 1f3e7ddd82..8952ae8e92 100644 --- a/drape_frontend/render_state.hpp +++ b/drape_frontend/render_state.hpp @@ -16,6 +16,7 @@ public: UserLineLayer, OverlayLayer, LocalAdsMarkLayer, + TransitSchemeLayer, UserMarkLayer, NavigationLayer, TransitMarkLayer, diff --git a/drape_frontend/shaders/shader_index.txt b/drape_frontend/shaders/shader_index.txt index 92627123b2..b14849c14e 100644 --- a/drape_frontend/shaders/shader_index.txt +++ b/drape_frontend/shaders/shader_index.txt @@ -20,6 +20,8 @@ TEXTURING_GUI_PROGRAM texturing_gui.vsh.glsl texturing.fsh.glsl RULER_PROGRAM ruler.vsh.glsl texturing.fsh.glsl ACCURACY_PROGRAM position_accuracy3d.vsh.glsl texturing.fsh.glsl MY_POSITION_PROGRAM my_position.vsh.glsl texturing.fsh.glsl +TRANSIT_PROGRAM transit.vsh.glsl transit.fsh.glsl +TRANSIT_MARKER_PROGRAM transit_marker.vsh.glsl transit_marker.fsh.glsl ROUTE_PROGRAM route.vsh.glsl route.fsh.glsl ROUTE_DASH_PROGRAM route.vsh.glsl route_dash.fsh.glsl ROUTE_ARROW_PROGRAM route_arrow.vsh.glsl route_arrow.fsh.glsl diff --git a/drape_frontend/shaders/shaders_lib.glsl b/drape_frontend/shaders/shaders_lib.glsl index 0cfbe35f57..ee317acc18 100644 --- a/drape_frontend/shaders/shaders_lib.glsl +++ b/drape_frontend/shaders/shaders_lib.glsl @@ -1,7 +1,7 @@ // This is a library of functions which we are use in our shaders. // Common (DO NOT modify this comment, it marks up block of common functions). -// Scale factor in shape's coordinates transformation from tile's coorinate +// Scale factor in shape's coordinates transformation from tile's coordinate // system. const float kShapeCoordScalar = 1000.0; diff --git a/drape_frontend/shaders/transit.fsh.glsl b/drape_frontend/shaders/transit.fsh.glsl new file mode 100644 index 0000000000..5bea707e16 --- /dev/null +++ b/drape_frontend/shaders/transit.fsh.glsl @@ -0,0 +1,10 @@ +#ifdef SAMSUNG_GOOGLE_NEXUS +uniform sampler2D u_colorTex; +#endif + +varying vec4 v_color; + +void main() +{ + gl_FragColor = v_color; +} diff --git a/drape_frontend/shaders/transit.vsh.glsl b/drape_frontend/shaders/transit.vsh.glsl new file mode 100644 index 0000000000..60511017c7 --- /dev/null +++ b/drape_frontend/shaders/transit.vsh.glsl @@ -0,0 +1,26 @@ +attribute vec3 a_position; +attribute vec4 a_normal; +attribute vec4 a_color; + +uniform mat4 modelView; +uniform mat4 projection; +uniform mat4 pivotTransform; + +uniform float u_lineHalfWidth; + +varying vec4 v_color; + +void main() +{ + vec2 normal = a_normal.xy; + vec2 transformedAxisPos = (vec4(a_position.xy, 0.0, 1.0) * modelView).xy; + if (dot(normal, normal) != 0.0) + { + vec2 norm = normal * u_lineHalfWidth; + transformedAxisPos = calcLineTransformedAxisPos(transformedAxisPos, a_position.xy + norm, + modelView, length(norm)); + } + v_color = a_color; + vec4 pos = vec4(transformedAxisPos, a_position.z, 1.0) * projection; + gl_Position = applyPivotTransform(pos, pivotTransform, 0.0); +} diff --git a/drape_frontend/shaders/transit_marker.fsh.glsl b/drape_frontend/shaders/transit_marker.fsh.glsl new file mode 100644 index 0000000000..14bea243c0 --- /dev/null +++ b/drape_frontend/shaders/transit_marker.fsh.glsl @@ -0,0 +1,22 @@ +#ifdef SAMSUNG_GOOGLE_NEXUS +uniform sampler2D u_colorTex; +#endif + +varying vec4 v_offsets; +varying vec4 v_color; +varying float v_scale; + +void main() +{ + vec4 finalColor = v_color; + vec2 radius; + radius.x = max(0.0, abs(v_offsets.x) - v_offsets.z); + radius.y = max(0.0, abs(v_offsets.y) - v_offsets.w); + + float maxRadius = 1.0; + float aaRadius = 0.9; + float stepValue = smoothstep(aaRadius * aaRadius, maxRadius * maxRadius, dot(radius.xy, radius.xy)); + finalColor.a = finalColor.a * (1.0 - stepValue); + + gl_FragColor = samsungGoogleNexusWorkaround(finalColor); +} diff --git a/drape_frontend/shaders/transit_marker.vsh.glsl b/drape_frontend/shaders/transit_marker.vsh.glsl new file mode 100644 index 0000000000..b028e50ee8 --- /dev/null +++ b/drape_frontend/shaders/transit_marker.vsh.glsl @@ -0,0 +1,26 @@ +attribute vec3 a_position; +attribute vec4 a_normal; +attribute vec4 a_color; + +uniform mat4 modelView; +uniform mat4 projection; +uniform mat4 pivotTransform; + +uniform float u_lineHalfWidth; +uniform vec2 u_angleCosSin; + +varying vec4 v_offsets; +varying vec4 v_color; + +void main() +{ + vec4 pos = vec4(a_position.xy, 0, 1) * modelView; + vec2 normal = vec2(a_normal.x * u_angleCosSin.x - a_normal.y * u_angleCosSin.y, + a_normal.x * u_angleCosSin.y + a_normal.y * u_angleCosSin.x); + vec2 shiftedPos = normal * u_lineHalfWidth + pos.xy; + pos = vec4(shiftedPos, a_position.z, 1.0) * projection; + gl_Position = applyPivotTransform(pos, pivotTransform, 0.0); + vec2 offsets = abs(a_normal.zw); + v_offsets = vec4(a_normal.zw, offsets - 1.0); + v_color = a_color; +} diff --git a/drape_frontend/text_layout.cpp b/drape_frontend/text_layout.cpp index 62186661a8..a4f1e8ffde 100644 --- a/drape_frontend/text_layout.cpp +++ b/drape_frontend/text_layout.cpp @@ -234,12 +234,13 @@ void CalculateOffsets(dp::Anchor anchor, float textRatio, dp::TextureManager::TGlyphsBuffer const & glyphs, buffer_vector const & delimIndexes, buffer_vector, 2> & result, - m2::PointF & pixelSize) + m2::PointF & pixelSize, size_t & rowsCount) { typedef pair TLengthAndHeight; buffer_vector lengthAndHeight; float maxLength = 0; float summaryHeight = 0; + rowsCount = 0; size_t start = 0; for (size_t index = 0; index < delimIndexes.size(); ++index) @@ -267,6 +268,8 @@ void CalculateOffsets(dp::Anchor anchor, float textRatio, } maxLength = max(maxLength, node.first); summaryHeight += node.second; + if (node.second > 0.0f) + ++rowsCount; start = end; } @@ -354,7 +357,7 @@ StraightTextLayout::StraightTextLayout(strings::UniString const & text, float fo delimIndexes.push_back(visibleText.size()); TBase::Init(visibleText, fontSize, isSdf, textures); - CalculateOffsets(anchor, m_textSizeRatio, m_metrics, delimIndexes, m_offsets, m_pixelSize); + CalculateOffsets(anchor, m_textSizeRatio, m_metrics, delimIndexes, m_offsets, m_pixelSize, m_rowsCount); } void StraightTextLayout::AdjustTextOffset(m2::PointF const & symbolSize, dp::Anchor textAnchor, dp::Anchor symbolAnchor, diff --git a/drape_frontend/text_layout.hpp b/drape_frontend/text_layout.hpp index 3390444a69..b135754d53 100644 --- a/drape_frontend/text_layout.hpp +++ b/drape_frontend/text_layout.hpp @@ -68,6 +68,7 @@ public: void CacheDynamicGeometry(glsl::vec2 const & pixelOffset, gpu::TTextDynamicVertexBuffer & dynamicBuffer) const; m2::PointF const & GetPixelSize() const { return m_pixelSize; } + size_t GetRowsCount() const { return m_rowsCount; } void AdjustTextOffset(m2::PointF const & symbolSize, dp::Anchor textAnchor, dp::Anchor symbolAnchor, glsl::vec2 & offset) const; @@ -90,6 +91,7 @@ private: glm::vec4 m_pivot; buffer_vector, 2> m_offsets; m2::PointF m_pixelSize; + size_t m_rowsCount = 0; }; class PathTextLayout : public TextLayout diff --git a/drape_frontend/traffic_generator.cpp b/drape_frontend/traffic_generator.cpp index 5fed6a1696..8ab7b3a372 100644 --- a/drape_frontend/traffic_generator.cpp +++ b/drape_frontend/traffic_generator.cpp @@ -177,7 +177,7 @@ void TrafficGenerator::FlushSegmentsGeometry(TileKey const & tileKey, TrafficSeg float const minU = kMinCoordU[static_cast(segmentColoringIt->second)]; int width = 0; - if (TrafficRenderer::CanBeRendereredAsLine(g.m_roadClass, tileKey.m_zoomLevel, width)) + if (TrafficRenderer::CanBeRenderedAsLine(g.m_roadClass, tileKey.m_zoomLevel, width)) { vector staticGeometry; GenerateLineSegment(colorRegion, g.m_polyline, tileKey.GetGlobalRect().Center(), depth, diff --git a/drape_frontend/traffic_renderer.cpp b/drape_frontend/traffic_renderer.cpp index 0270e324b6..9f865f2e0c 100644 --- a/drape_frontend/traffic_renderer.cpp +++ b/drape_frontend/traffic_renderer.cpp @@ -250,7 +250,7 @@ float TrafficRenderer::GetTwoWayOffset(RoadClass const & roadClass, int zoomLeve float TrafficRenderer::GetPixelWidth(RoadClass const & roadClass, int zoomLevel) { int width = 0; - if (CanBeRendereredAsLine(roadClass, zoomLevel, width)) + if (CanBeRenderedAsLine(roadClass, zoomLevel, width)) return static_cast(width); return GetPixelWidthInternal(roadClass, zoomLevel); @@ -274,7 +274,7 @@ float TrafficRenderer::GetPixelWidthInternal(RoadClass const & roadClass, int zo } // static -bool TrafficRenderer::CanBeRendereredAsLine(RoadClass const & roadClass, int zoomLevel, int & width) +bool TrafficRenderer::CanBeRenderedAsLine(RoadClass const & roadClass, int zoomLevel, int & width) { if (roadClass == RoadClass::Class0) return false; diff --git a/drape_frontend/traffic_renderer.hpp b/drape_frontend/traffic_renderer.hpp index e79aa65e25..16eb4a8377 100644 --- a/drape_frontend/traffic_renderer.hpp +++ b/drape_frontend/traffic_renderer.hpp @@ -36,7 +36,7 @@ public: void OnGeometryReady(int currentZoomLevel); static float GetTwoWayOffset(RoadClass const & roadClass, int zoomLevel); - static bool CanBeRendereredAsLine(RoadClass const & roadClass, int zoomLevel, int & width); + static bool CanBeRenderedAsLine(RoadClass const & roadClass, int zoomLevel, int & width); private: static float GetPixelWidth(RoadClass const & roadClass, int zoomLevel); diff --git a/drape_frontend/transit_scheme_builder.cpp b/drape_frontend/transit_scheme_builder.cpp new file mode 100644 index 0000000000..5e2b64325d --- /dev/null +++ b/drape_frontend/transit_scheme_builder.cpp @@ -0,0 +1,807 @@ +#include "transit_scheme_builder.hpp" + +#include "drape_frontend/color_constants.hpp" +#include "drape_frontend/colored_symbol_shape.hpp" +#include "drape_frontend/line_shape_helper.hpp" +#include "drape_frontend/map_shape.hpp" +#include "drape_frontend/render_state.hpp" +#include "drape_frontend/shader_def.hpp" +#include "drape_frontend/shape_view_params.hpp" +#include "drape_frontend/text_layout.hpp" +#include "drape_frontend/text_shape.hpp" +#include "drape_frontend/visual_params.hpp" + +#include "drape/batcher.hpp" +#include "drape/glsl_func.hpp" +#include "drape/glsl_types.hpp" +#include "drape/render_bucket.hpp" +#include "drape/utils/vertex_decl.hpp" + +#include "base/string_utils.hpp" + +using namespace std; + +#define TRANSIT_SCHEME_DEBUG_INFO + +namespace df +{ + +std::vector const kTransitLinesWidthInPixel = + { + // 1 2 3 4 5 6 7 8 9 10 + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + //11 12 13 14 15 16 17 18 19 20 + 1.5f, 1.8f, 2.2f, 2.8f, 3.2f, 3.8f, 4.8f, 5.2f, 5.8f, 5.8f + }; + +namespace +{ +float const kBaseLineDepth = 0.0f; +float const kDepthPerLine = 1.0f; +float const kBaseMarkerDepth = 300.0f; +float const kBaseTitleDepth = 400.0f; + +float const kOuterMarkerDepth = kBaseMarkerDepth + 0.5f; +float const kInnerMarkerDepth = kBaseMarkerDepth + 1.0f; +uint32_t const kTransitOverlayIndex = 1000; + +// TODO(@darina) Use separate colors. +std::string const kTransitMarkText = "RouteMarkPrimaryText"; +std::string const kTransitMarkTextOutline = "RouteMarkPrimaryTextOutline"; +std::string const kTransitTransferOuterColor = "RouteMarkPrimaryText"; +std::string const kTransitStopInnerColor = "RouteMarkPrimaryTextOutline"; + +float const kTransitMarkTextSize = 12.0f; + +struct TransitLineStaticVertex +{ + using TPosition = glsl::vec3; + using TNormal = glsl::vec4; + using TColor = glsl::vec4; + + TransitLineStaticVertex() = default; + TransitLineStaticVertex(TPosition const & position, TNormal const & normal, + TColor const & color) + : m_position(position), m_normal(normal), m_color(color) {} + + TPosition m_position; + TNormal m_normal; + TColor m_color; +}; + +struct SchemeSegment +{ + glsl::vec2 m_p1; + glsl::vec2 m_p2; + glsl::vec2 m_tangent; + glsl::vec2 m_leftNormal; + glsl::vec2 m_rightNormal; +}; +using TGeometryBuffer = std::vector; + +dp::BindingInfo const & GetTransitStaticBindingInfo() +{ + static unique_ptr s_info; + if (s_info == nullptr) + { + dp::BindingFiller filler(3); + filler.FillDecl("a_position"); + filler.FillDecl("a_normal"); + filler.FillDecl("a_color"); + s_info.reset(new dp::BindingInfo(filler.m_info)); + } + return *s_info; +} + +void SubmitStaticVertex(glsl::vec3 const & pivot, glsl::vec2 const & normal, float side, + glsl::vec4 const & color, TGeometryBuffer & geometry) +{ + geometry.emplace_back(pivot, TransitLineStaticVertex::TNormal(normal, side, 0.0), color); +} + +void GenerateJoinsTriangles(glsl::vec3 const & pivot, + std::vector const & normals, + glsl::vec2 const & offset, + glsl::vec4 const & color, + TGeometryBuffer & joinsGeometry) +{ + size_t const trianglesCount = normals.size() / 3; + for (size_t j = 0; j < trianglesCount; j++) + { + SubmitStaticVertex(pivot, normals[3 * j] + offset, 1.0f, color, joinsGeometry); + SubmitStaticVertex(pivot, normals[3 * j + 1] + offset, 1.0f, color, joinsGeometry); + SubmitStaticVertex(pivot, normals[3 * j + 2] + offset, 1.0f, color, joinsGeometry); + } +} + +struct TitleInfo +{ + TitleInfo() = default; + TitleInfo(std::string const & text) + : m_text(text) + {} + + std::string m_text; + size_t m_rowsCount = 0; + m2::PointF m_pixelSize; + m2::PointF m_offset; + dp::Anchor m_anchor = dp::Left; +}; + +std::vector PlaceTitles(StopNodeParams const & stopParams, float textSize, ref_ptr textures) +{ + std::vector titles; + for (auto const & stopInfo : stopParams.m_stopsInfo) + { + if (stopInfo.second.m_name.empty()) + continue; + bool isUnique = true; + for (auto const & title : titles) + { + if (title.m_text == stopInfo.second.m_name) + { + isUnique = false; + break; + } + } + if (isUnique) + titles.emplace_back(stopInfo.second.m_name); + } + + if (titles.size() > 1) + { + auto const vs = static_cast(df::VisualParams::Instance().GetVisualScale()); + + size_t summaryRowsCount = 0; + for (auto & name : titles) + { + df::StraightTextLayout layout(strings::MakeUniString(name.m_text), textSize, + false /* isSdf */, textures, dp::Left); + name.m_pixelSize = layout.GetPixelSize() + m2::PointF(4.0f * vs, 4.0f * vs); + name.m_rowsCount = layout.GetRowsCount(); + summaryRowsCount += layout.GetRowsCount(); + } + + auto const rightRowsCount = summaryRowsCount > 3 ? (summaryRowsCount + 1) / 2 : summaryRowsCount; + float rightHeight = 0.0f; + float leftHeight = 0.0f; + size_t rowsCount = 0; + size_t rightTitlesCount = 0; + for (size_t i = 0; i < titles.size(); ++i) + { + if (rowsCount < rightRowsCount) + { + rightHeight += titles[i].m_pixelSize.y; + ++rightTitlesCount; + } + else + { + leftHeight += titles[i].m_pixelSize.y; + } + rowsCount += titles[i].m_rowsCount; + } + + float currentOffset = -rightHeight / 2.0f; + for (size_t i = 0; i < rightTitlesCount; ++i) + { + titles[i].m_anchor = dp::Left; + titles[i].m_offset.y = currentOffset + titles[i].m_pixelSize.y / 2.0f; + currentOffset += titles[i].m_pixelSize.y; + } + + currentOffset = -leftHeight / 2.0f; + for (size_t i = rightTitlesCount; i < titles.size(); ++i) + { + titles[i].m_anchor = dp::Right; + titles[i].m_offset.y = currentOffset + titles[i].m_pixelSize.y / 2.0f; + currentOffset += titles[i].m_pixelSize.y; + } + } + return titles; +} + +vector GetTransitMarkerSizes(float markerScale, float maxRouteWidth) +{ + auto const vs = static_cast(df::VisualParams::Instance().GetVisualScale()); + vector markerSizes; + markerSizes.reserve(df::kTransitLinesWidthInPixel.size()); + for (auto const halfWidth : df::kTransitLinesWidthInPixel) + { + float const d = 2.0f * std::min(halfWidth * vs, maxRouteWidth * 0.5f) * markerScale; + markerSizes.push_back(m2::PointF(d, d)); + } + return markerSizes; +} + +uint32_t GetRouteId(routing::transit::LineId lineId) +{ + return static_cast(lineId >> 4); +} + +void FillStopParams(TransitDisplayInfo const & transitDisplayInfo, MwmSet::MwmId const & mwmId, + routing::transit::Stop const & stop, StopNodeParams & stopParams) +{ + FeatureID featureId; + std::string title; + if (stop.GetFeatureId() != routing::transit::kInvalidFeatureId) + { + featureId = FeatureID(mwmId, stop.GetFeatureId()); + title = transitDisplayInfo.m_features.at(featureId).m_title; + } + stopParams.m_isTransfer = false; + stopParams.m_pivot = stop.GetPoint(); + for (auto lineId : stop.GetLineIds()) + { + StopInfo & info = stopParams.m_stopsInfo[GetRouteId(lineId)]; + info.m_featureId = featureId; + info.m_name = title; + info.m_lines.insert(lineId); + } +} +} // namespace + +void TransitSchemeBuilder::SetVisibleMwms(std::vector const & visibleMwms) +{ + m_visibleMwms = visibleMwms; +} + +void TransitSchemeBuilder::UpdateScheme(TransitDisplayInfos const & transitDisplayInfos) +{ + for (auto const & mwmInfo : transitDisplayInfos) + { + if (!mwmInfo.second) + continue; + + auto const & mwmId = mwmInfo.first; + auto const & transitDisplayInfo = *mwmInfo.second.get(); + + MwmSchemeData & scheme = m_schemes[mwmId]; + + CollectStops(transitDisplayInfo, mwmId, scheme); + CollectLines(transitDisplayInfo, scheme); + CollectShapes(transitDisplayInfo, scheme); + + PrepareScheme(scheme); + } +} + +void TransitSchemeBuilder::Clear() +{ + m_schemes.clear(); +} + +void TransitSchemeBuilder::Clear(MwmSet::MwmId const & mwmId) +{ + m_schemes.erase(mwmId); +} + +void TransitSchemeBuilder::BuildScheme(ref_ptr textures) +{ + ++m_recacheId; + for (auto const & mwmId : m_visibleMwms) + { + if (m_schemes.find(mwmId) == m_schemes.end()) + continue; + GenerateShapes(mwmId); + GenerateStops(mwmId, textures); + } +} + +void TransitSchemeBuilder::CollectStops(TransitDisplayInfo const & transitDisplayInfo, + MwmSet::MwmId const & mwmId, MwmSchemeData & scheme) +{ + for (auto const & stopInfo : transitDisplayInfo.m_stops) + { + routing::transit::Stop const & stop = stopInfo.second; + if (stop.GetTransferId() != routing::transit::kInvalidTransferId) + continue; + auto & stopNode = scheme.m_stops[stop.GetId()]; + FillStopParams(transitDisplayInfo, mwmId, stop, stopNode); + } + + for (auto const & stopInfo : transitDisplayInfo.m_transfers) + { + routing::transit::Transfer const & transfer = stopInfo.second; + auto & stopNode = scheme.m_transfers[transfer.GetId()]; + + for (auto stopId : transfer.GetStopIds()) + { + if (transitDisplayInfo.m_stops.find(stopId) == transitDisplayInfo.m_stops.end()) + { + LOG(LWARNING, ("Invalid stop", stopId, "in transfer", transfer.GetId())); + continue; + } + routing::transit::Stop const & stop = transitDisplayInfo.m_stops.at(stopId); + FillStopParams(transitDisplayInfo, mwmId, stop, stopNode); + } + stopNode.m_isTransfer = true; + stopNode.m_pivot = transfer.GetPoint(); + } +} + +void TransitSchemeBuilder::CollectLines(TransitDisplayInfo const & transitDisplayInfo, MwmSchemeData & scheme) +{ + std::multimap linesLengths; + for (auto const & line : transitDisplayInfo.m_lines) + { + auto const lineId = line.second.GetId(); + size_t stopsCount = 0; + auto const & stopsRanges = line.second.GetStopIds(); + for (auto const & stops : stopsRanges) + stopsCount += stops.size(); + linesLengths.insert(std::make_pair(stopsCount, lineId)); + } + float depth = kBaseLineDepth; + for (auto const & pair : linesLengths) + { + auto const lineId = pair.second; + scheme.m_lines[lineId] = LineParams(transitDisplayInfo.m_lines.at(lineId).GetColor(), depth); + depth += kDepthPerLine; + } +} + +void TransitSchemeBuilder::CollectShapes(TransitDisplayInfo const & transitDisplayInfo, MwmSchemeData & scheme) +{ + std::map> roads; + for (auto const & line : transitDisplayInfo.m_lines) + { + auto const lineId = line.second.GetId(); + auto const roadId = GetRouteId(lineId); + roads[roadId].push_back(lineId); + } + + for (auto const & line : transitDisplayInfo.m_lines) + { + auto const lineId = line.second.GetId(); + auto const roadId = GetRouteId(lineId); + + auto const & stopsRanges = line.second.GetStopIds(); + for (auto const & stops : stopsRanges) + { + for (size_t i = 0; i < stops.size(); ++i) + { + if (i + 1 < stops.size()) + { + bool shapeAdded = false; + + auto const & sameLines = roads[roadId]; + for (auto const & sameLineId : sameLines) + { + if (sameLineId == lineId) + continue; + + auto const & sameLine = transitDisplayInfo.m_lines.at(sameLineId); + auto const & sameStopsRanges = sameLine.GetStopIds(); + for (auto const & sameStops : sameStopsRanges) + { + size_t stop1Ind = std::numeric_limits::max(); + size_t stop2Ind = std::numeric_limits::max(); + + for (size_t stopInd = 0; stopInd < sameStops.size(); ++stopInd) + { + if (sameStops[stopInd] == stops[i]) + stop1Ind = stopInd; + else if (sameStops[stopInd] == stops[i + 1]) + stop2Ind = stopInd; + } + + if (stop1Ind < sameStops.size() || stop2Ind < sameStops.size()) + { + if (stop1Ind > stop2Ind) + swap(stop1Ind, stop2Ind); + + if (stop1Ind < sameStops.size() && stop2Ind < sameStops.size() && stop2Ind - stop1Ind > 1) + { + for (size_t stopInd = stop1Ind; stopInd < stop2Ind; ++stopInd) + { + shapeAdded = true; + AddShape(transitDisplayInfo, sameStops[stopInd], sameStops[stopInd + 1], lineId, scheme); + } + } + break; + } + } + + if (shapeAdded) + break; + } + + if (!shapeAdded) + AddShape(transitDisplayInfo, stops[i], stops[i + 1], lineId, scheme); + } + } + } + } +} + +void TransitSchemeBuilder::AddShape(TransitDisplayInfo const & transitDisplayInfo, + routing::transit::StopId stop1Id, + routing::transit::StopId stop2Id, + routing::transit::LineId lineId, + MwmSchemeData & scheme) +{ + auto const stop1It = transitDisplayInfo.m_stops.find(stop1Id); + ASSERT(stop1It != transitDisplayInfo.m_stops.end(), ()); + + auto const stop2It = transitDisplayInfo.m_stops.find(stop2Id); + ASSERT(stop2It != transitDisplayInfo.m_stops.end(), ()); + + auto const transfer1Id = stop1It->second.GetTransferId(); + auto const transfer2Id = stop2It->second.GetTransferId(); + + auto shapeId = routing::transit::ShapeId(transfer1Id != routing::transit::kInvalidTransferId ? transfer1Id + : stop1Id, + transfer2Id != routing::transit::kInvalidTransferId ? transfer2Id + : stop2Id); + auto it = transitDisplayInfo.m_shapes.find(shapeId); + bool isForward = true; + if (it == transitDisplayInfo.m_shapes.end()) + { + isForward = false; + shapeId = routing::transit::ShapeId(shapeId.GetStop2Id(), shapeId.GetStop1Id()); + it = transitDisplayInfo.m_shapes.find(shapeId); + } + + // TODO: check + if (it == transitDisplayInfo.m_shapes.end()) + return; + + ASSERT(it != transitDisplayInfo.m_shapes.end(), ()); + + auto itScheme = scheme.m_shapes.find(shapeId); + if (itScheme == scheme.m_shapes.end()) + { + auto const & polyline = transitDisplayInfo.m_shapes.at(it->first).GetPolyline(); + if (isForward) + scheme.m_shapes[shapeId].m_forwardLines.push_back(lineId); + else + scheme.m_shapes[shapeId].m_backwardLines.push_back(lineId); + scheme.m_shapes[shapeId].m_polyline = polyline; + } + else + { + for (auto id : itScheme->second.m_forwardLines) + if (GetRouteId(id) == GetRouteId(lineId)) + return; + for (auto id : itScheme->second.m_backwardLines) + if (GetRouteId(id) == GetRouteId(lineId)) + return; + + if (isForward) + itScheme->second.m_forwardLines.push_back(lineId); + else + itScheme->second.m_backwardLines.push_back(lineId); + } +} + +void TransitSchemeBuilder::PrepareScheme(MwmSchemeData & scheme) +{ + m2::RectD boundingRect; + for (auto const & shape : scheme.m_shapes) + { + auto const stop1 = shape.first.GetStop1Id(); + auto const stop2 = shape.first.GetStop2Id(); + StopNodeParams & params1 = (scheme.m_stops.find(stop1) == scheme.m_stops.end()) ? scheme.m_transfers[stop1] + : scheme.m_stops[stop1]; + StopNodeParams & params2 = (scheme.m_stops.find(stop2) == scheme.m_stops.end()) ? scheme.m_transfers[stop2] + : scheme.m_stops[stop2]; + + auto const linesCount = shape.second.m_forwardLines.size() + shape.second.m_backwardLines.size(); + + auto const sz = shape.second.m_polyline.size(); + + auto const dir1 = (shape.second.m_polyline[1] - shape.second.m_polyline[0]).Normalize(); + auto const dir2 = (shape.second.m_polyline[sz - 2] - shape.second.m_polyline[sz - 1]).Normalize(); + + params1.m_shapesInfo[shape.first].m_linesCount = linesCount; + params1.m_shapesInfo[shape.first].m_direction = dir1; + + params2.m_shapesInfo[shape.first].m_linesCount = linesCount; + params2.m_shapesInfo[shape.first].m_direction = dir2; + + for (auto const & pt : shape.second.m_polyline) + boundingRect.Add(pt); + } + scheme.m_pivot = boundingRect.Center(); +} + +void TransitSchemeBuilder::GenerateShapes(MwmSet::MwmId const & mwmId) +{ + MwmSchemeData const & scheme = m_schemes[mwmId]; + + auto const state = CreateGLState(gpu::TRANSIT_PROGRAM, RenderState::TransitSchemeLayer); + TransitRenderData renderData(state, m_recacheId, mwmId, scheme.m_pivot); + + uint32_t const kBatchSize = 5000; + dp::Batcher batcher(kBatchSize, kBatchSize); + { + dp::SessionGuard guard(batcher, [&renderData](dp::GLState const & state, drape_ptr && b) + { + renderData.m_buckets.push_back(std::move(b)); + }); + + for (auto const & shape : scheme.m_shapes) + { + auto const linesCount = shape.second.m_forwardLines.size() + shape.second.m_backwardLines.size(); + auto shapeOffset = -static_cast(linesCount / 2) * 2.0f - 1.0f * static_cast(linesCount % 2) + 1.0f; + auto const shapeOffsetIncrement = 2.0f; + auto const lineHalfWidth = 0.8f; + + std::vector> coloredLines; + for (auto lineId : shape.second.m_forwardLines) + { + auto const colorName = df::GetTransitColorName(scheme.m_lines.at(lineId).m_color); + auto const color = GetColorConstant(colorName); + coloredLines.push_back(std::make_pair(color, lineId)); + } + for (auto it = shape.second.m_backwardLines.rbegin(); it != shape.second.m_backwardLines.rend(); ++it) + { + auto const colorName = df::GetTransitColorName(scheme.m_lines.at(*it).m_color); + auto const color = GetColorConstant(colorName); + coloredLines.push_back(std::make_pair(color, *it)); + } + + for (auto const & coloredLine : coloredLines) + { + auto const & colorConst = coloredLine.first; + auto const & lineId = coloredLine.second; + auto const depth = scheme.m_lines.at(lineId).m_depth; + + GenerateLine(shape.second.m_polyline, scheme.m_pivot, colorConst, shapeOffset, lineHalfWidth, depth, batcher); + shapeOffset += shapeOffsetIncrement; + } + } + } + m_flushRenderDataFn(std::move(renderData)); +} + +void TransitSchemeBuilder::GenerateStops(MwmSet::MwmId const & mwmId, ref_ptr textures) +{ + MwmSchemeData const & scheme = m_schemes[mwmId]; + + uint32_t const kBatchSize = 5000; + dp::Batcher batcher(kBatchSize, kBatchSize); + { + dp::SessionGuard guard(batcher, + [this, &mwmId, &scheme](dp::GLState const & state, drape_ptr && b) + { + TransitRenderData renderData(state, m_recacheId, mwmId, scheme.m_pivot); + renderData.m_buckets.emplace_back(std::move(b)); + if (state.GetProgramIndex() == gpu::TRANSIT_MARKER_PROGRAM) + { + m_flushMarkersRenderDataFn(std::move(renderData)); + } + else if (state.GetProgramIndex() == gpu::TEXT_OUTLINED_PROGRAM) + { + m_flushTextRenderDataFn(std::move(renderData)); + } + else + { + m_flushStubsRenderDataFn(std::move(renderData)); + } + }); + + float const kStopScale = 2.5f; + float const kTransferScale = 3.0f; + std::vector const transferMarkerSizes = GetTransitMarkerSizes(kTransferScale, 1000); + std::vector const stopMarkerSizes = GetTransitMarkerSizes(kStopScale, 1000); + + for (auto const & stop : scheme.m_stops) + { + GenerateStop(stop.second, scheme.m_pivot, scheme.m_lines, batcher); + GenerateTitles(stop.second, scheme.m_pivot, stopMarkerSizes, textures, batcher); + } + for (auto const & transfer : scheme.m_transfers) + { + GenerateTransfer(transfer.second, scheme.m_pivot, batcher); + GenerateTitles(transfer.second, scheme.m_pivot, transferMarkerSizes, textures, batcher); + } + } +} + +void TransitSchemeBuilder::GenerateTransfer(StopNodeParams const & params, m2::PointD const & pivot, dp::Batcher & batcher) +{ + m2::PointD const pt = MapShape::ConvertToLocal(params.m_pivot, pivot, kShapeCoordScalar); + + size_t maxLinesCount = 0; + m2::PointD dir; + for (auto const & shapeInfo : params.m_shapesInfo) + { + if (shapeInfo.second.m_linesCount > maxLinesCount) + { + dir = shapeInfo.second.m_direction; + maxLinesCount = shapeInfo.second.m_linesCount; + } + } + + float const kInnerScale = 1.0f; + float const kOuterScale = 1.5f; + auto const outerColor = GetColorConstant(kTransitTransferOuterColor); + auto const innerColor = GetColorConstant(kTransitStopInnerColor); + + float const widthLinesCount = maxLinesCount > 3 ? 1.6f : 1.0f; + float const innerScale = maxLinesCount == 1 ? 1.4f : kInnerScale; + float const outerScale = maxLinesCount == 1 ? 1.9f : kOuterScale; + + GenerateMarker(pt, dir, widthLinesCount, maxLinesCount, outerScale, outerScale, + kOuterMarkerDepth, outerColor, batcher); + + GenerateMarker(pt, dir, widthLinesCount, maxLinesCount, innerScale, innerScale, + kInnerMarkerDepth, innerColor, batcher); +} + +void TransitSchemeBuilder::GenerateStop(StopNodeParams const & params, m2::PointD const & pivot, + std::map const & lines, + dp::Batcher & batcher) +{ + bool severalRoads = params.m_stopsInfo.size() > 1; + + if (severalRoads) + { + GenerateTransfer(params, pivot, batcher); + return; + } + + float const kInnerScale = 0.8f; + float const kOuterScale = 2.0f; + + auto const lineId = *params.m_stopsInfo.begin()->second.m_lines.begin(); + auto const colorName = df::GetTransitColorName(lines.at(lineId).m_color); + auto const outerColor = GetColorConstant(colorName); + + auto const innerColor = GetColorConstant(kTransitStopInnerColor); + + m2::PointD const pt = MapShape::ConvertToLocal(params.m_pivot, pivot, kShapeCoordScalar); + + m2::PointD dir = params.m_shapesInfo.begin()->second.m_direction; + + GenerateMarker(pt, dir, 1.0f, 1.0f, kOuterScale, kOuterScale, kOuterMarkerDepth, outerColor, batcher); + + GenerateMarker(pt, dir, 1.0f, 1.0f, kInnerScale, kInnerScale, kInnerMarkerDepth, innerColor, batcher); +} + +void TransitSchemeBuilder::GenerateTitles(StopNodeParams const & stopParams, m2::PointD const & pivot, + vector const & markerSizes, + ref_ptr textures, dp::Batcher & batcher) +{ + auto const vs = static_cast(df::VisualParams::Instance().GetVisualScale()); + + auto const titles = PlaceTitles(stopParams, kTransitMarkTextSize * vs, textures); + if (titles.empty()) + return; + + auto const featureId = stopParams.m_stopsInfo.begin()->second.m_featureId; + auto priority = static_cast(stopParams.m_isTransfer ? Priority::TransferMin : Priority::StopMin); + priority += static_cast(stopParams.m_stopsInfo.size()); + + dp::TitleDecl titleDecl; + titleDecl.m_primaryOptional = true; + titleDecl.m_primaryTextFont.m_color = df::GetColorConstant(kTransitMarkText); + titleDecl.m_primaryTextFont.m_outlineColor = df::GetColorConstant(kTransitMarkTextOutline); + titleDecl.m_primaryTextFont.m_size = kTransitMarkTextSize * vs; + titleDecl.m_anchor = dp::Left; + + for (auto const & title : titles) + { + TextViewParams textParams; + textParams.m_featureID = featureId; + textParams.m_tileCenter = pivot; + textParams.m_titleDecl = titleDecl; + textParams.m_titleDecl.m_primaryText = title.m_text; + textParams.m_titleDecl.m_anchor = title.m_anchor; + textParams.m_depth = kBaseTitleDepth; + textParams.m_depthLayer = RenderState::TransitSchemeLayer; + textParams.m_specialDisplacement = SpecialDisplacement::SpecialMode; + textParams.m_specialPriority = priority; + textParams.m_startOverlayRank = dp::OverlayRank1; + + TextShape(stopParams.m_pivot, textParams, TileKey(), markerSizes, title.m_offset, dp::Center, kTransitOverlayIndex) + .Draw(&batcher, textures); + } + + df::ColoredSymbolViewParams colorParams; + colorParams.m_radiusInPixels = markerSizes.front().x * 0.5f; + colorParams.m_color = dp::Color::Transparent(); + colorParams.m_featureID = featureId; + colorParams.m_tileCenter = pivot; + colorParams.m_depth = kBaseTitleDepth; + colorParams.m_depthLayer = RenderState::TransitSchemeLayer; + colorParams.m_specialDisplacement = SpecialDisplacement::SpecialMode; + colorParams.m_specialPriority = priority; + colorParams.m_startOverlayRank = dp::OverlayRank0; + + ColoredSymbolShape(stopParams.m_pivot, colorParams, TileKey(), kTransitOverlayIndex, markerSizes) + .Draw(&batcher, textures); +} + +void TransitSchemeBuilder::GenerateMarker(m2::PointD const & pt, m2::PointD widthDir, + float linesCountWidth, float linesCountHeight, + float scaleWidth, float scaleHeight, + float depth, dp::Color const & color, dp::Batcher & batcher) +{ + using TV = TransitLineStaticVertex; + + scaleWidth = (scaleWidth - 1.0f) / linesCountWidth + 1.0f; + scaleHeight = (scaleHeight - 1.0f) / linesCountHeight + 1.0f; + + widthDir.y = -widthDir.y; + m2::PointD heightDir = widthDir.Ort(); + + auto const v1 = -widthDir * scaleWidth * linesCountWidth - heightDir * scaleHeight * linesCountHeight; + auto const v2 = widthDir * scaleWidth * linesCountWidth - heightDir * scaleHeight * linesCountHeight; + auto const v3 = widthDir * scaleWidth * linesCountWidth + heightDir * scaleHeight * linesCountHeight; + auto const v4 = -widthDir * scaleWidth * linesCountWidth + heightDir * scaleHeight * linesCountHeight; + + glsl::vec3 const pos(pt.x, pt.y, depth); + auto const colorVal = glsl::vec4(color.GetRedF(), color.GetGreenF(), color.GetBlueF(), 1.0f /* alpha */ ); + + TGeometryBuffer geometry; + geometry.reserve(6); + geometry.emplace_back(pos, TV::TNormal(v1.x, v1.y, -linesCountWidth, -linesCountHeight), colorVal); + geometry.emplace_back(pos, TV::TNormal(v2.x, v2.y, linesCountWidth, -linesCountHeight), colorVal); + geometry.emplace_back(pos, TV::TNormal(v3.x, v3.y, linesCountWidth, linesCountHeight), colorVal); + geometry.emplace_back(pos, TV::TNormal(v1.x, v1.y, -linesCountWidth, -linesCountHeight), colorVal); + geometry.emplace_back(pos, TV::TNormal(v3.x, v3.y, linesCountWidth, linesCountHeight), colorVal); + geometry.emplace_back(pos, TV::TNormal(v4.x, v4.y, -linesCountWidth, linesCountHeight), colorVal); + + dp::AttributeProvider provider(1 /* stream count */, static_cast(geometry.size())); + provider.InitStream(0 /* stream index */, GetTransitStaticBindingInfo(), make_ref(geometry.data())); + auto state = CreateGLState(gpu::TRANSIT_MARKER_PROGRAM, RenderState::TransitSchemeLayer); + batcher.InsertTriangleList(state, make_ref(&provider)); +} + +void TransitSchemeBuilder::GenerateLine(std::vector const & path, m2::PointD const & pivot, + dp::Color const & colorConst, float lineOffset, float lineHalfWidth, + float depth, dp::Batcher & batcher) +{ + TGeometryBuffer geometry; + auto const color = glsl::vec4(colorConst.GetRedF(), colorConst.GetGreenF(), colorConst.GetBlueF(), 1.0f /* alpha */); + size_t const kAverageSize = path.size() * 6; + size_t const kAverageCapSize = 12; + geometry.reserve(kAverageSize + kAverageCapSize * 2); + + std::vector segments; + segments.reserve(path.size() - 1); + + for (size_t i = 1; i < path.size(); ++i) + { + if (path[i].EqualDxDy(path[i - 1], 1.0E-5)) + continue; + + SchemeSegment segment; + segment.m_p1 = glsl::ToVec2(MapShape::ConvertToLocal(path[i - 1], pivot, kShapeCoordScalar)); + segment.m_p2 = glsl::ToVec2(MapShape::ConvertToLocal(path[i], pivot, kShapeCoordScalar)); + CalculateTangentAndNormals(segment.m_p1, segment.m_p2, segment.m_tangent, + segment.m_leftNormal, segment.m_rightNormal); + + auto const startPivot = glsl::vec3(segment.m_p1, depth); + auto const endPivot = glsl::vec3(segment.m_p2, depth); + auto const offset = lineOffset * segment.m_rightNormal; + + SubmitStaticVertex(startPivot, segment.m_rightNormal * lineHalfWidth - offset, -lineHalfWidth, color, geometry); + SubmitStaticVertex(startPivot, segment.m_leftNormal * lineHalfWidth - offset, lineHalfWidth, color, geometry); + SubmitStaticVertex(endPivot, segment.m_rightNormal * lineHalfWidth - offset, -lineHalfWidth, color, geometry); + SubmitStaticVertex(endPivot, segment.m_rightNormal * lineHalfWidth - offset, -lineHalfWidth, color, geometry); + SubmitStaticVertex(startPivot, segment.m_leftNormal * lineHalfWidth - offset, lineHalfWidth, color, geometry); + SubmitStaticVertex(endPivot, segment.m_leftNormal * lineHalfWidth - offset, lineHalfWidth, color, geometry); + + segments.emplace_back(std::move(segment)); + } + + for (size_t i = 0; i < segments.size(); ++i) + { + int const kSegmentsCount = 4; + vector normals; + normals.reserve(kAverageCapSize); + GenerateCapNormals(dp::RoundCap, segments[i].m_leftNormal, segments[i].m_rightNormal, segments[i].m_tangent, + lineHalfWidth, false /* isStart */, normals, kSegmentsCount); + GenerateJoinsTriangles(glsl::vec3(segments[i].m_p2, depth), normals, -lineOffset * segments[i].m_rightNormal, + color, geometry); + } + + dp::AttributeProvider provider(1 /* stream count */, static_cast(geometry.size())); + provider.InitStream(0 /* stream index */, GetTransitStaticBindingInfo(), make_ref(geometry.data())); + auto state = CreateGLState(gpu::TRANSIT_PROGRAM, RenderState::TransitSchemeLayer); + batcher.InsertTriangleList(state, make_ref(&provider)); +} +} // namespace df diff --git a/drape_frontend/transit_scheme_builder.hpp b/drape_frontend/transit_scheme_builder.hpp new file mode 100644 index 0000000000..e93e7ceb4c --- /dev/null +++ b/drape_frontend/transit_scheme_builder.hpp @@ -0,0 +1,167 @@ +#pragma once + +#include "drape/batcher.hpp" +#include "drape/glstate.hpp" +#include "drape/render_bucket.hpp" +#include "drape/texture_manager.hpp" + +#include "transit/transit_display_info.hpp" + +#include +#include +#include +#include + +namespace df +{ +int constexpr kTransitSchemeMinZoomLevel = 10; + +extern std::vector const kTransitLinesWidthInPixel; + +struct TransitRenderData +{ + dp::GLState m_state; + uint32_t m_recacheId; + MwmSet::MwmId m_mwmId; + m2::PointD m_pivot; + std::vector> m_buckets; + + TransitRenderData(dp::GLState const & state, uint32_t recacheId, MwmSet::MwmId const & mwmId, m2::PointD const pivot) + : m_state(state) + , m_recacheId(recacheId) + , m_mwmId(mwmId) + , m_pivot(pivot) + {} + TransitRenderData(TransitRenderData &&) = default; + TransitRenderData & operator=(TransitRenderData &&) = default; +}; + +using TTransitRenderData = std::vector; + +struct LineParams +{ + LineParams() = default; + LineParams(string const & color, float depth) + : m_color(color), m_depth(depth) + {} + std::string m_color; + float m_depth; +}; + +struct ShapeParams +{ + std::vector m_forwardLines; + std::vector m_backwardLines; + std::vector m_polyline; +}; + +struct ShapeInfo +{ + m2::PointD m_direction; + size_t m_linesCount; +}; + +struct StopInfo +{ + StopInfo() = default; + StopInfo(std::string const & name, FeatureID const & featureId) + : m_name(name) + , m_featureId(featureId) + {} + + std::string m_name; + FeatureID m_featureId; + std::set m_lines; +}; + +struct StopNodeParams +{ + bool m_isTransfer = false; + m2::PointD m_pivot; + std::map m_shapesInfo; + std::map m_stopsInfo; +}; + +class TransitSchemeBuilder +{ +public: + enum class Priority: uint16_t + { + Default = 0, + StopMin = 1, + StopMax = 30, + TransferMin = 31, + TransferMax = 60 + }; + + using TFlushRenderDataFn = function; + + TransitSchemeBuilder(TFlushRenderDataFn const & flushFn, + TFlushRenderDataFn const & flushMarkersFn, + TFlushRenderDataFn const & flushTextFn, + TFlushRenderDataFn const & flushStubsFn) + : m_flushRenderDataFn(flushFn) + , m_flushMarkersRenderDataFn(flushMarkersFn) + , m_flushTextRenderDataFn(flushTextFn) + , m_flushStubsRenderDataFn(flushStubsFn) + {} + + void SetVisibleMwms(std::vector const & visibleMwms); + + void UpdateScheme(TransitDisplayInfos const & transitDisplayInfos); + void BuildScheme(ref_ptr textures); + + void Clear(); + void Clear(MwmSet::MwmId const & mwmId); + +private: + struct MwmSchemeData + { + m2::PointD m_pivot; + + std::map m_lines; + std::map m_shapes; + std::map m_stops; + std::map m_transfers; + }; + + void CollectStops(TransitDisplayInfo const & transitDisplayInfo, + MwmSet::MwmId const & mwmId, MwmSchemeData & scheme); + + void CollectLines(TransitDisplayInfo const & transitDisplayInfo, MwmSchemeData & scheme); + + void CollectShapes(TransitDisplayInfo const & transitDisplayInfo, MwmSchemeData & scheme); + void AddShape(TransitDisplayInfo const & transitDisplayInfo, routing::transit::StopId stop1Id, + routing::transit::StopId stop2Id, routing::transit::LineId lineId, MwmSchemeData & scheme); + + void PrepareScheme(MwmSchemeData & scheme); + + void GenerateShapes(MwmSet::MwmId const & mwmId); + void GenerateStops(MwmSet::MwmId const & mwmId, ref_ptr textures); + + void GenerateMarker(m2::PointD const & pt, m2::PointD widthDir, float linesCountWidth, float linesCountHeight, + float scaleWidth, float scaleHeight, float depth, dp::Color const & color, dp::Batcher & batcher); + + void GenerateTransfer(StopNodeParams const & params, m2::PointD const & pivot, dp::Batcher & batcher); + + void GenerateStop(StopNodeParams const & params, m2::PointD const & pivot, + std::map const & lines, dp::Batcher & batcher); + + void GenerateTitles(StopNodeParams const & params, m2::PointD const & pivot, vector const & markerSizes, + ref_ptr textures, dp::Batcher & batcher); + + void GenerateLine(std::vector const & path, m2::PointD const & pivot, dp::Color const & colorConst, + float lineOffset, float lineHalfWidth, float depth, dp::Batcher & batcher); + + using TransitSchemes = std::map; + TransitSchemes m_schemes; + std::vector m_visibleMwms; + + TFlushRenderDataFn m_flushRenderDataFn; + TFlushRenderDataFn m_flushMarkersRenderDataFn; + TFlushRenderDataFn m_flushTextRenderDataFn; + TFlushRenderDataFn m_flushStubsRenderDataFn; + + uint32_t m_recacheId = 0; +}; +} // namespace df diff --git a/drape_frontend/transit_scheme_renderer.cpp b/drape_frontend/transit_scheme_renderer.cpp new file mode 100644 index 0000000000..7a95634a0b --- /dev/null +++ b/drape_frontend/transit_scheme_renderer.cpp @@ -0,0 +1,247 @@ +#include "drape_frontend/transit_scheme_renderer.hpp" + +#include "drape_frontend/postprocess_renderer.hpp" +#include "drape_frontend/shader_def.hpp" +#include "drape_frontend/shape_view_params.hpp" +#include "drape_frontend/visual_params.hpp" + +#include "drape/overlay_tree.hpp" +#include "drape/vertex_array_buffer.hpp" + +namespace df +{ +namespace +{ + +float CalculateHalfWidth(ScreenBase const & screen) +{ + double zoom = 0.0; + int index = 0; + float lerpCoef = 0.0f; + ExtractZoomFactors(screen, zoom, index, lerpCoef); + + return InterpolateByZoomLevels(index, lerpCoef, kTransitLinesWidthInPixel) + * static_cast(VisualParams::Instance().GetVisualScale()); +} +} // namespace + +bool TransitSchemeRenderer::HasRenderData(int zoomLevel) const +{ + return !m_renderData.empty() && zoomLevel >= kTransitSchemeMinZoomLevel; +} + +void TransitSchemeRenderer::ClearGLDependentResources() +{ + m_renderData.clear(); + m_markersRenderData.clear(); + m_textRenderData.clear(); + m_colorSymbolRenderData.clear(); +} + +void TransitSchemeRenderer::Clear(MwmSet::MwmId const & mwmId) +{ + ClearRenderData(mwmId, m_renderData); + ClearRenderData(mwmId, m_markersRenderData); + ClearRenderData(mwmId, m_textRenderData); + ClearRenderData(mwmId, m_colorSymbolRenderData); +} + +void TransitSchemeRenderer::ClearRenderData(MwmSet::MwmId const & mwmId, std::vector & renderData) +{ + auto removePredicate = [&mwmId](TransitRenderData const & data) { return data.m_mwmId == mwmId; }; + + renderData.erase(remove_if(renderData.begin(), renderData.end(), removePredicate), + renderData.end()); +} + +void TransitSchemeRenderer::AddRenderData(ref_ptr mng, + TransitRenderData && renderData) +{ + PrepareRenderData(mng, m_renderData, renderData); +} + +void TransitSchemeRenderer::AddMarkersRenderData(ref_ptr mng, + TransitRenderData && renderData) +{ + PrepareRenderData(mng, m_markersRenderData, renderData); +} + +void TransitSchemeRenderer::AddTextRenderData(ref_ptr mng, + TransitRenderData && renderData) +{ + PrepareRenderData(mng, m_textRenderData, renderData); +} + +void TransitSchemeRenderer::AddStubsRenderData(ref_ptr mng, + TransitRenderData && renderData) +{ + PrepareRenderData(mng, m_colorSymbolRenderData, renderData); +} + +void TransitSchemeRenderer::PrepareRenderData(ref_ptr mng, + std::vector & currentRenderData, + TransitRenderData & newRenderData) +{ + // Remove obsolete render data. + currentRenderData.erase(remove_if(currentRenderData.begin(), currentRenderData.end(), + [this, &newRenderData](TransitRenderData const & rd) + { + return rd.m_mwmId == newRenderData.m_mwmId && rd.m_recacheId < m_lastRecacheId; + }), currentRenderData.end()); + + m_lastRecacheId = max(m_lastRecacheId, newRenderData.m_recacheId); + + // Add new render data. + currentRenderData.emplace_back(std::move(newRenderData)); + TransitRenderData & rd = currentRenderData.back(); + + ref_ptr program = mng->GetProgram(rd.m_state.GetProgramIndex()); + program->Bind(); + for (auto const & bucket : rd.m_buckets) + bucket->GetBuffer()->Build(program); +} + +void TransitSchemeRenderer::RenderTransit(ScreenBase const & screen, int zoomLevel, + ref_ptr mng, + ref_ptr postprocessRenderer, + dp::UniformValuesStorage const & commonUniforms) +{ + if (!HasRenderData(zoomLevel)) + return; + + float const pixelHalfWidth = CalculateHalfWidth(screen); + + RenderLines(screen, mng, commonUniforms, pixelHalfWidth); + RenderMarkers(screen, mng, commonUniforms, pixelHalfWidth); + { + StencilWriterGuard guard(postprocessRenderer); + RenderText(screen, mng, commonUniforms); + } + RenderStubs(screen, mng, commonUniforms); +} + +void TransitSchemeRenderer::CollectOverlays(ref_ptr tree, ScreenBase const & modelView) +{ + CollectOverlays(tree, modelView, m_textRenderData); + CollectOverlays(tree, modelView, m_colorSymbolRenderData); +} + +void TransitSchemeRenderer::CollectOverlays(ref_ptr tree, ScreenBase const & modelView, + std::vector & renderData) +{ + for (auto & data : renderData) + { + for (auto const & bucket : data.m_buckets) + { + if (tree->IsNeedUpdate()) + bucket->CollectOverlayHandles(tree); + else + bucket->Update(modelView); + } + } +} + +void TransitSchemeRenderer::RenderLines(ScreenBase const & screen, ref_ptr mng, + dp::UniformValuesStorage const & commonUniforms, float pixelHalfWidth) +{ + GLFunctions::glDisable(gl_const::GLDepthTest); + for (auto & renderData : m_renderData) + { + ref_ptr program = mng->GetProgram(renderData.m_state.GetProgramIndex()); + program->Bind(); + dp::ApplyState(renderData.m_state, program); + + dp::UniformValuesStorage uniforms = commonUniforms; + math::Matrix mv = screen.GetModelView(renderData.m_pivot, kShapeCoordScalar); + uniforms.SetMatrix4x4Value("modelView", mv.m_data); + + uniforms.SetFloatValue("u_lineHalfWidth", pixelHalfWidth); + dp::ApplyUniforms(uniforms, program); + + for (auto const & bucket : renderData.m_buckets) + bucket->Render(false /* draw as line */); + } +} + +void TransitSchemeRenderer::RenderMarkers(ScreenBase const & screen, ref_ptr mng, + dp::UniformValuesStorage const & commonUniforms, float pixelHalfWidth) +{ + GLFunctions::glEnable(gl_const::GLDepthTest); + for (auto & renderData : m_markersRenderData) + { + ref_ptr program = mng->GetProgram(renderData.m_state.GetProgramIndex()); + program->Bind(); + dp::ApplyState(renderData.m_state, program); + + dp::UniformValuesStorage uniforms = commonUniforms; + math::Matrix mv = screen.GetModelView(renderData.m_pivot, kShapeCoordScalar); + uniforms.SetMatrix4x4Value("modelView", mv.m_data); + uniforms.SetFloatValue("u_lineHalfWidth", pixelHalfWidth); + uniforms.SetFloatValue("u_angleCosSin", + static_cast(cos(screen.GetAngle())), + static_cast(sin(screen.GetAngle()))); + dp::ApplyUniforms(uniforms, program); + + for (auto const & bucket : renderData.m_buckets) + bucket->Render(false /* draw as line */); + } +} + +void TransitSchemeRenderer::RenderText(ScreenBase const & screen, ref_ptr mng, + dp::UniformValuesStorage const & commonUniforms) +{ + auto const & params = df::VisualParams::Instance().GetGlyphVisualParams(); + for (auto & renderData : m_textRenderData) + { + ref_ptr program = mng->GetProgram(renderData.m_state.GetProgramIndex()); + program->Bind(); + dp::ApplyState(renderData.m_state, program); + + dp::UniformValuesStorage uniforms = commonUniforms; + math::Matrix mv = screen.GetModelView(renderData.m_pivot, kShapeCoordScalar); + uniforms.SetMatrix4x4Value("modelView", mv.m_data); + uniforms.SetFloatValue("u_opacity", 1.0); + + uniforms.SetFloatValue("u_contrastGamma", params.m_outlineContrast, params.m_outlineGamma); + uniforms.SetFloatValue("u_isOutlinePass", 1.0f); + dp::ApplyUniforms(uniforms, program); + + for (auto const & bucket : renderData.m_buckets) + bucket->Render(false /* draw as line */); + + uniforms.SetFloatValue("u_contrastGamma", params.m_contrast, params.m_gamma); + uniforms.SetFloatValue("u_isOutlinePass", 0.0f); + dp::ApplyUniforms(uniforms, program); + + for (auto const & bucket : renderData.m_buckets) + bucket->Render(false /* draw as line */); + + for (auto const & bucket : renderData.m_buckets) + bucket->RenderDebug(screen); + } +} + +void TransitSchemeRenderer::RenderStubs(ScreenBase const & screen, ref_ptr mng, + dp::UniformValuesStorage const & commonUniforms) +{ + for (auto & renderData : m_colorSymbolRenderData) + { + ref_ptr program = mng->GetProgram(renderData.m_state.GetProgramIndex()); + program->Bind(); + dp::ApplyState(renderData.m_state, program); + + dp::UniformValuesStorage uniforms = commonUniforms; + math::Matrix mv = screen.GetModelView(renderData.m_pivot, kShapeCoordScalar); + uniforms.SetMatrix4x4Value("modelView", mv.m_data); + uniforms.SetFloatValue("u_opacity", 1.0); + dp::ApplyUniforms(uniforms, program); + + GLFunctions::glEnable(gl_const::GLDepthTest); + for (auto const & bucket : renderData.m_buckets) + bucket->Render(false /* draw as line */); + + for (auto const & bucket : renderData.m_buckets) + bucket->RenderDebug(screen); + } +} +} // namespace df diff --git a/drape_frontend/transit_scheme_renderer.hpp b/drape_frontend/transit_scheme_renderer.hpp new file mode 100644 index 0000000000..4d9d966365 --- /dev/null +++ b/drape_frontend/transit_scheme_renderer.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include "drape_frontend/transit_scheme_builder.hpp" + +#include "drape/gpu_program_manager.hpp" +#include "drape/pointers.hpp" +#include "drape/uniform_values_storage.hpp" + +#include "geometry/screenbase.hpp" + +namespace df +{ +class PostprocessRenderer; + +class TransitSchemeRenderer +{ +public: + void AddRenderData(ref_ptr mng, TransitRenderData && renderData); + void AddMarkersRenderData(ref_ptr mng, TransitRenderData && renderData); + void AddTextRenderData(ref_ptr mng, TransitRenderData && renderData); + void AddStubsRenderData(ref_ptr mng, TransitRenderData && renderData); + + bool HasRenderData(int zoomLevel) const; + + void RenderTransit(ScreenBase const & screen, int zoomLevel, + ref_ptr mng, + ref_ptr postprocessRenderer, + dp::UniformValuesStorage const & commonUniforms); + + void CollectOverlays(ref_ptr tree, ScreenBase const & modelView); + + void ClearGLDependentResources(); + + void Clear(MwmSet::MwmId const & mwmId); + +private: + void PrepareRenderData(ref_ptr mng, std::vector & currentRenderData, + TransitRenderData & newRenderData); + void ClearRenderData(MwmSet::MwmId const & mwmId, std::vector & renderData); + + void CollectOverlays(ref_ptr tree, ScreenBase const & modelView, + std::vector & renderData); + + void RenderLines(ScreenBase const & screen, ref_ptr mng, + dp::UniformValuesStorage const & commonUniforms, float pixelHalfWidth); + void RenderMarkers(ScreenBase const & screen, ref_ptr mng, + dp::UniformValuesStorage const & commonUniforms, float pixelHalfWidth); + void RenderText(ScreenBase const & screen, ref_ptr mng, + dp::UniformValuesStorage const & commonUniforms); + void RenderStubs(ScreenBase const & screen, ref_ptr mng, + dp::UniformValuesStorage const & commonUniforms); + + uint32_t m_lastRecacheId = 0; + std::vector m_renderData; + std::vector m_markersRenderData; + std::vector m_textRenderData; + std::vector m_colorSymbolRenderData; +}; +} // namespace df diff --git a/map/framework.cpp b/map/framework.cpp index 8924a4b301..8ffbe11b69 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -126,6 +126,7 @@ char const kAllow3dKey[] = "Allow3d"; char const kAllow3dBuildingsKey[] = "Buildings3d"; char const kAllowAutoZoom[] = "AutoZoom"; char const kTrafficEnabledKey[] = "TrafficEnabled"; +char const kTransitSchemeEnabledKey[] = "TransitSchemeEnabled"; char const kTrafficSimplifiedColorsKey[] = "TrafficSimplifiedColors"; char const kLargeFontsSize[] = "LargeFontsSize"; char const kTranslitMode[] = "TransliterationMode"; @@ -269,6 +270,7 @@ void Framework::OnViewportChanged(ScreenBase const & screen) GetBookmarkManager().UpdateViewport(m_currentModelView); m_trafficManager.UpdateViewport(m_currentModelView); m_localAdsManager.UpdateViewport(m_currentModelView); + m_transitManager.UpdateViewport(m_currentModelView); if (m_viewportChanged != nullptr) m_viewportChanged(screen); @@ -356,15 +358,17 @@ Framework::Framework(FrameworkParams const & params) , m_storage(platform::migrate::NeedMigrate() ? COUNTRIES_OBSOLETE_FILE : COUNTRIES_FILE) , m_enabledDiffs(params.m_enableDiffs) , m_isRenderingEnabled(true) + , m_transitManager(m_model.GetIndex(), + [this](FeatureCallback const & fn, vector const & features) + { + return m_model.ReadFeatures(fn, features); + }, + bind(&Framework::GetMwmsByRect, this, _1, false /* rough */)) , m_routingManager( RoutingManager::Callbacks( [this]() -> Index & { return m_model.GetIndex(); }, [this]() -> storage::CountryInfoGetter & { return GetCountryInfoGetter(); }, [this](string const & id) -> string { return m_storage.GetParentIdFor(id); }, - [this](RoutingManager::Callbacks::FeatureCallback const & fn, - vector const & features) { - return m_model.ReadFeatures(fn, features); - }, [this]() -> StringsBundle const & { return m_stringsBundle; }), static_cast(*this)) , m_trafficManager(bind(&Framework::GetMwmsByRect, this, _1, false /* rough */), @@ -433,6 +437,8 @@ Framework::Framework(FrameworkParams const & params) m_bmManager->SetInvalidTokenHandler([this] { m_user.ResetAccessToken(); }); m_user.AddSubscriber(m_bmManager->GetUserSubscriber()); + m_routingManager.SetTransitManager(&m_transitManager); + InitCityFinder(); InitDiscoveryManager(); InitTaxiEngine(); @@ -570,6 +576,7 @@ void Framework::OnCountryFileDownloaded(storage::TCountryId const & countryId, s rect = id.GetInfo()->m_limitRect; } m_trafficManager.Invalidate(); + m_transitManager.Invalidate(); m_localAdsManager.OnDownloadCountry(countryId); InvalidateRect(rect); GetSearchAPI().ClearCaches(); @@ -617,6 +624,7 @@ void Framework::OnMapDeregistered(platform::LocalCountryFile const & localFile) auto const mwmId = m_model.GetIndex().GetMwmIdByCountryFile(localFile.GetCountryFile()); m_trafficManager.OnMwmDeregistered(mwmId); + m_transitManager.OnMwmDeregistered(mwmId); } bool Framework::HasUnsavedEdits(storage::TCountryId const & countryId) @@ -1761,6 +1769,7 @@ void Framework::CreateDrapeEngine(ref_ptr contextFactory, m_drapeApi.SetDrapeEngine(make_ref(m_drapeEngine)); m_routingManager.SetDrapeEngine(make_ref(m_drapeEngine), allow3d); m_trafficManager.SetDrapeEngine(make_ref(m_drapeEngine)); + m_transitManager.SetDrapeEngine(make_ref(m_drapeEngine)); m_localAdsManager.SetDrapeEngine(make_ref(m_drapeEngine)); m_searchMarks.SetDrapeEngine(make_ref(m_drapeEngine)); @@ -1783,6 +1792,7 @@ void Framework::OnRecoverGLContext(int width, int height) } m_trafficManager.OnRecoverGLContext(); + m_transitManager.Invalidate(); m_localAdsManager.Invalidate(); } @@ -1803,6 +1813,7 @@ void Framework::DestroyDrapeEngine() m_drapeApi.SetDrapeEngine(nullptr); m_routingManager.SetDrapeEngine(nullptr, false); m_trafficManager.SetDrapeEngine(nullptr); + m_transitManager.SetDrapeEngine(nullptr); m_localAdsManager.SetDrapeEngine(nullptr); m_searchMarks.SetDrapeEngine(nullptr); GetBookmarkManager().SetDrapeEngine(nullptr); @@ -2524,6 +2535,26 @@ void Framework::SaveAutoZoom(bool allowAutoZoom) settings::Set(kAllowAutoZoom, allowAutoZoom); } +void Framework::EnableTransitScheme(bool enable) +{ + m_transitManager.EnableTransitSchemeMode(enable); + if (m_drapeEngine != nullptr) + m_drapeEngine->EnableTransitScheme(enable); +} + +bool Framework::LoadTransitSchemeEnabled() +{ + bool enabled; + if (!settings::Get(kTransitSchemeEnabledKey, enabled)) + enabled = true; + return enabled; +} + +void Framework::SaveTransitSchemeEnabled(bool enabled) +{ + settings::Set(kTransitSchemeEnabledKey, enabled); +} + void Framework::EnableChoosePositionMode(bool enable, bool enableBounds, bool applyPosition, m2::PointD const & position) { if (m_drapeEngine != nullptr) @@ -2647,7 +2678,16 @@ bool Framework::ParseDrapeDebugCommand(string const & query) m_drapeEngine->EnableUGCRendering(false /* enabled */); return true; } - + if (query == "?scheme") + { + EnableTransitScheme(true /* enable */); + return true; + } + if (query == "?no-scheme") + { + EnableTransitScheme(false /* enable */); + return true; + } return false; } diff --git a/map/framework.hpp b/map/framework.hpp index 5bd31099a2..dd51dad2ed 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -17,6 +17,7 @@ #include "map/search_mark.hpp" #include "map/track.hpp" #include "map/traffic_manager.hpp" +#include "map/transit/transit_reader.hpp" #include "map/user.hpp" #include "drape_frontend/gui/skin.hpp" @@ -205,6 +206,8 @@ protected: bool m_isRenderingEnabled; + TransitReadManager m_transitManager; + // Note. |m_routingManager| should be declared before |m_trafficManager| RoutingManager m_routingManager; @@ -749,6 +752,10 @@ public: bool LoadTrafficSimplifiedColors(); void SaveTrafficSimplifiedColors(bool simplified); + void EnableTransitScheme(bool enable); + bool LoadTransitSchemeEnabled(); + void SaveTransitSchemeEnabled(bool enabled); + public: template uint32_t Discover(discovery::ClientParams && params, ResultCallback const & onResult, diff --git a/map/routing_manager.cpp b/map/routing_manager.cpp index 7412aed43d..6ee463daf2 100644 --- a/map/routing_manager.cpp +++ b/map/routing_manager.cpp @@ -222,7 +222,6 @@ RoutingManager::RoutingManager(Callbacks && callbacks, Delegate & delegate) tracking::Reporter::kPushDelayMs) , m_extrapolator( [this](location::GpsInfo const & gpsInfo) { this->OnExtrapolatedLocationUpdate(gpsInfo); }) - , m_transitReadManager(m_callbacks.m_indexGetter(), m_callbacks.m_readFeaturesFn) { auto const routingStatisticsFn = [](map const & statistics) { alohalytics::LogEvent("Routing_CalculatingRoute", statistics); @@ -273,6 +272,11 @@ void RoutingManager::SetBookmarkManager(BookmarkManager * bmManager) m_bmManager = bmManager; } +void RoutingManager::SetTransitManager(TransitReadManager * transitManager) +{ + m_transitReadManager = transitManager; +} + void RoutingManager::OnBuildRouteReady(Route const & route, RouterResultCode code) { // Hide preview. @@ -453,7 +457,7 @@ void RoutingManager::InsertRoute(Route const & route) { return m_callbacks.m_indexGetter().GetMwmIdByCountryFile(numMwmIds->GetFile(numMwmId)); }; - transitRouteDisplay = make_shared(m_transitReadManager, getMwmId, + transitRouteDisplay = make_shared(*m_transitReadManager, getMwmId, m_callbacks.m_stringsBundleGetter, m_bmManager, m_transitSymbolSizes); } diff --git a/map/routing_manager.hpp b/map/routing_manager.hpp index 34504894dd..aad752acd2 100644 --- a/map/routing_manager.hpp +++ b/map/routing_manager.hpp @@ -70,26 +70,21 @@ public: using IndexGetterFn = std::function; using CountryInfoGetterFn = std::function; using CountryParentNameGetterFn = std::function; - using FeatureCallback = std::function; - using ReadFeaturesFn = std::function const &)>; using GetStringsBundleFn = std::function; template + typename StringsBundleGetter> Callbacks(IndexGetter && featureIndexGetter, CountryInfoGetter && countryInfoGetter, - CountryParentNameGetter && countryParentNameGetter, - FeatureReader && readFeatures, StringsBundleGetter && stringsBundleGetter) + CountryParentNameGetter && countryParentNameGetter, StringsBundleGetter && stringsBundleGetter) : m_indexGetter(std::forward(featureIndexGetter)) , m_countryInfoGetter(std::forward(countryInfoGetter)) , m_countryParentNameGetterFn(std::forward(countryParentNameGetter)) - , m_readFeaturesFn(std::forward(readFeatures)) , m_stringsBundleGetter(std::forward(stringsBundleGetter)) {} IndexGetterFn m_indexGetter; CountryInfoGetterFn m_countryInfoGetter; CountryParentNameGetterFn m_countryParentNameGetterFn; - TReadFeaturesFn m_readFeaturesFn; GetStringsBundleFn m_stringsBundleGetter; }; @@ -108,6 +103,7 @@ public: RoutingManager(Callbacks && callbacks, Delegate & delegate); void SetBookmarkManager(BookmarkManager * bmManager); + void SetTransitManager(TransitReadManager * transitManager); routing::RoutingSession const & RoutingSession() const { return m_routingSession; } routing::RoutingSession & RoutingSession() { return m_routingSession; } @@ -311,7 +307,7 @@ private: std::chrono::steady_clock::time_point m_loadRoutePointsTimestamp; std::map m_transitSymbolSizes; - TransitReadManager m_transitReadManager; + TransitReadManager * m_transitReadManager = nullptr; DECLARE_THREAD_CHECKER(m_threadChecker); }; diff --git a/map/transit/transit_reader.cpp b/map/transit/transit_reader.cpp index 110c307ac6..1204b9be03 100644 --- a/map/transit/transit_reader.cpp +++ b/map/transit/transit_reader.cpp @@ -8,13 +8,30 @@ #include "coding/reader.hpp" +#include "drape_frontend/drape_engine.hpp" #include "drape_frontend/stylist.hpp" +#include "drape_frontend/visual_params.hpp" #include "base/stl_add.hpp" using namespace routing; using namespace std; +namespace +{ +int constexpr kMinSchemeZoomLevel = 10; +size_t constexpr kMaxTransitCacheSizeBytes = 5 /* Mb */ * 1024 * 1024; + +size_t CalculateCacheSize(TransitDisplayInfo const & transitInfo) +{ + size_t const kSegmentSize = 72; + size_t cacheSize = 0; + for (auto const & shape : transitInfo.m_shapes) + cacheSize += shape.second.GetPolyline().size() * kSegmentSize; + return cacheSize; +} +} // namespace + // ReadTransitTask -------------------------------------------------------------------------------- void ReadTransitTask::Init(uint64_t id, MwmSet::MwmId const & mwmId, unique_ptr transitInfo) @@ -46,6 +63,11 @@ void ReadTransitTask::Do() return; } MwmValue const & mwmValue = *handle.GetValue(); + if (!m_loadSubset && !mwmValue.m_cont.IsExist(TRANSIT_FILE_TAG)) + { + m_success = true; + return; + } CHECK(mwmValue.m_cont.IsExist(TRANSIT_FILE_TAG), ("No transit section in mwm, but transit route was built with it. mwmId:", m_mwmId)); @@ -64,7 +86,7 @@ void ReadTransitTask::Do() m_transitInfo->m_features[featureId] = {}; } - if (stop.second.GetTransferId() != transit::kInvalidTransferId) + if (m_loadSubset && (stop.second.GetTransferId() != transit::kInvalidTransferId)) m_transitInfo->m_transfers[stop.second.GetTransferId()] = {}; } FillItemsByIdMap(graphData.GetTransfers(), m_transitInfo->m_transfers); @@ -109,9 +131,11 @@ unique_ptr && ReadTransitTask::GetTransitInfo() return move(m_transitInfo); } -TransitReadManager::TransitReadManager(Index & index, TReadFeaturesFn const & readFeaturesFn) +TransitReadManager::TransitReadManager(Index & index, TReadFeaturesFn const & readFeaturesFn, + GetMwmsByRectFn const & getMwmsByRectFn) : m_index(index) , m_readFeaturesFn(readFeaturesFn) + , m_getMwmsByRectFn(getMwmsByRectFn) { Start(); } @@ -139,6 +163,130 @@ void TransitReadManager::Stop() m_threadsPool.reset(); } +void TransitReadManager::SetDrapeEngine(ref_ptr engine) +{ + m_drapeEngine.Set(engine); +} + +void TransitReadManager::EnableTransitSchemeMode(bool enable) +{ + if (m_isSchemeMode == enable) + return; + m_isSchemeMode = enable; + if (!m_isSchemeMode) + { + m_lastVisibleMwms.clear(); + m_lastActiveMwms.clear(); + m_mwmCache.clear(); + m_cacheSize = 0; + } + else + { + Invalidate(); + } +} + +void TransitReadManager::UpdateViewport(ScreenBase const & screen) +{ + m_currentModelView = {screen, true /* initialized */}; + + if (!m_isSchemeMode) + return; + + if (df::GetZoomLevel(screen.GetScale()) < kMinSchemeZoomLevel) + return; + + auto mwms = m_getMwmsByRectFn(screen.ClipRect()); + if (m_lastVisibleMwms == mwms) + return; + + m_lastVisibleMwms = mwms; + m_lastActiveMwms.clear(); + + auto const currentTime = steady_clock::now(); + TransitDisplayInfos displayInfos; + for (auto const & mwmId : mwms) + { + if (!mwmId.IsAlive()) + continue; + m_lastActiveMwms.insert(mwmId); + auto it = m_mwmCache.find(mwmId); + if (it == m_mwmCache.end()) + { + displayInfos[mwmId] = {}; + m_mwmCache.insert(make_pair(mwmId, CacheEntry(currentTime))); + } + else + { + it->second.m_lastActiveTime = currentTime; + } + } + GetTransitDisplayInfo(displayInfos); + if (!displayInfos.empty()) + { + for (auto const & transitInfo : displayInfos) + { + if (transitInfo.second != nullptr) + { + auto it = m_mwmCache.find(transitInfo.first); + it->second.m_isLoaded = true; + it->second.m_dataSize = CalculateCacheSize(*transitInfo.second); + m_cacheSize += it->second.m_dataSize; + + } + } + ShrinkCacheToAllowableSize(); + m_drapeEngine.SafeCall(&df::DrapeEngine::UpdateTransitScheme, + std::move(displayInfos), mwms); + } +} + +void TransitReadManager::ClearCache(MwmSet::MwmId const & mwmId) +{ + auto it = m_mwmCache.find(mwmId); + if (it == m_mwmCache.end()) + return; + m_cacheSize -= it->second.m_dataSize; + m_mwmCache.erase(it); + m_drapeEngine.SafeCall(&df::DrapeEngine::ClearTransitSchemeCache, mwmId); +} + +void TransitReadManager::OnMwmDeregistered(MwmSet::MwmId const & mwmId) +{ + ClearCache(mwmId); +} + +void TransitReadManager::Invalidate() +{ + if (!m_isSchemeMode) + return; + + m_lastVisibleMwms.clear(); + + if (m_currentModelView.second) + UpdateViewport(m_currentModelView.first); +} + +void TransitReadManager::ShrinkCacheToAllowableSize() +{ + using namespace std::chrono; + if (m_cacheSize > kMaxTransitCacheSizeBytes) + { + std::multimap, MwmSet::MwmId> seenTimings; + for (auto const & entry : m_mwmCache) + { + if (entry.second.m_isLoaded && m_lastActiveMwms.count(entry.first) == 0) + seenTimings.insert(make_pair(entry.second.m_lastActiveTime, entry.first)); + } + + while (m_cacheSize > kMaxTransitCacheSizeBytes && !seenTimings.empty()) + { + ClearCache(seenTimings.begin()->second); + seenTimings.erase(seenTimings.begin()); + } + } +} + bool TransitReadManager::GetTransitDisplayInfo(TransitDisplayInfos & transitDisplayInfos) { unique_lock lock(m_mutex); @@ -166,16 +314,17 @@ bool TransitReadManager::GetTransitDisplayInfo(TransitDisplayInfos & transitDisp m_tasksGroups.erase(groupId); lock.unlock(); + bool result = true; for (auto const & transitTask : transitTasks) { if (!transitTask.second->GetSuccess()) - return false; - } - - for (auto const & transitTask : transitTasks) + { + result = false; + continue; + } transitDisplayInfos[transitTask.first] = transitTask.second->GetTransitInfo(); - - return true; + } + return result; } void TransitReadManager::OnTaskCompleted(threads::IRoutine * task) diff --git a/map/transit/transit_reader.hpp b/map/transit/transit_reader.hpp index 3318570811..0c75af2ef4 100644 --- a/map/transit/transit_reader.hpp +++ b/map/transit/transit_reader.hpp @@ -1,6 +1,10 @@ #pragma once -#include "transit/transit_types.hpp" +#include "transit/transit_display_info.hpp" + +#include "drape_frontend/drape_engine_safe_ptr.hpp" + +#include "geometry/screenbase.hpp" #include "indexer/feature_decl.hpp" #include "indexer/index.hpp" @@ -8,43 +12,19 @@ #include "base/thread.hpp" #include "base/thread_pool.hpp" +#include #include #include #include #include #include #include +#include #include #include -struct TransitFeatureInfo -{ - bool m_isGate = false; - std::string m_gateSymbolName; - std::string m_title; - m2::PointD m_point; -}; - -using TransitFeaturesInfo = std::map; - -using TransitStopsInfo = std::map; -using TransitTransfersInfo = std::map; -using TransitShapesInfo = std::map; -using TransitLinesInfo = std::map; -using TransitNetworksInfo = std::map; - -struct TransitDisplayInfo -{ - TransitNetworksInfo m_networks; - TransitLinesInfo m_lines; - TransitStopsInfo m_stops; - TransitTransfersInfo m_transfers; - TransitShapesInfo m_shapes; - TransitFeaturesInfo m_features; -}; - -template using TReadCallback = std::function; -using TReadFeaturesFn = std::function const & , std::vector const &)>; +using FeatureCallback = std::function; +using TReadFeaturesFn = std::function const &)>; class ReadTransitTask: public threads::IRoutine { @@ -95,25 +75,34 @@ private: bool m_success = false; }; -using TransitDisplayInfos = std::map>; - class TransitReadManager { public: - TransitReadManager(Index & index, TReadFeaturesFn const & readFeaturesFn); + using GetMwmsByRectFn = function(m2::RectD const &)>; + + + TransitReadManager(Index & index, TReadFeaturesFn const & readFeaturesFn, + GetMwmsByRectFn const & getMwmsByRectFn); ~TransitReadManager(); void Start(); void Stop(); + void SetDrapeEngine(ref_ptr engine); + bool GetTransitDisplayInfo(TransitDisplayInfos & transitDisplayInfos); - // TODO(@darina) Clear cache for deleted mwm. - //void OnMwmDeregistered(MwmSet::MwmId const & mwmId); + void EnableTransitSchemeMode(bool enable); + void UpdateViewport(ScreenBase const & screen); + void OnMwmDeregistered(MwmSet::MwmId const & mwmId); + void Invalidate(); private: void OnTaskCompleted(threads::IRoutine * task); + void ShrinkCacheToAllowableSize(); + void ClearCache(MwmSet::MwmId const & mwmId); + std::unique_ptr m_threadsPool; std::mutex m_mutex; @@ -124,6 +113,25 @@ private: Index & m_index; TReadFeaturesFn m_readFeaturesFn; - // TODO(@darina) In case of reading the whole mwm transit section, save it in the cache for transit scheme rendering. - //TransitDisplayInfos m_transitDisplayCache; + + df::DrapeEngineSafePtr m_drapeEngine; + + struct CacheEntry + { + CacheEntry(std::chrono::time_point const & activeTime) + : m_lastActiveTime(activeTime) + {} + + bool m_isLoaded = false; + size_t m_dataSize = 0; + std::chrono::time_point m_lastActiveTime; + }; + + GetMwmsByRectFn m_getMwmsByRectFn; + std::vector m_lastVisibleMwms; + std::set m_lastActiveMwms; + std::map m_mwmCache; + size_t m_cacheSize = 0; + bool m_isSchemeMode = false; + pair m_currentModelView = {ScreenBase(), false /* initialized */}; }; diff --git a/transit/CMakeLists.txt b/transit/CMakeLists.txt index 7b06eb21b3..f4f2e6b7c9 100644 --- a/transit/CMakeLists.txt +++ b/transit/CMakeLists.txt @@ -6,6 +6,7 @@ include_directories( set( SRC + transit_display_info.hpp transit_graph_data.cpp transit_graph_data.hpp transit_serdes.hpp diff --git a/transit/transit_display_info.hpp b/transit/transit_display_info.hpp new file mode 100644 index 0000000000..8bd40a7586 --- /dev/null +++ b/transit/transit_display_info.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "transit/transit_types.hpp" + +#include "indexer/feature_decl.hpp" + +#include +#include + +struct TransitFeatureInfo +{ + bool m_isGate = false; + std::string m_gateSymbolName; + std::string m_title; + m2::PointD m_point; +}; + +using TransitFeaturesInfo = std::map; + +using TransitStopsInfo = std::map; +using TransitTransfersInfo = std::map; +using TransitShapesInfo = std::map; +using TransitLinesInfo = std::map; +using TransitNetworksInfo = std::map; + +struct TransitDisplayInfo +{ + TransitNetworksInfo m_networks; + TransitLinesInfo m_lines; + TransitStopsInfo m_stops; + TransitTransfersInfo m_transfers; + TransitShapesInfo m_shapes; + TransitFeaturesInfo m_features; +}; + +using TransitDisplayInfos = std::map>; diff --git a/xcode/drape_frontend/drape_frontend.xcodeproj/project.pbxproj b/xcode/drape_frontend/drape_frontend.xcodeproj/project.pbxproj index eee62ed617..3d6a04de29 100644 --- a/xcode/drape_frontend/drape_frontend.xcodeproj/project.pbxproj +++ b/xcode/drape_frontend/drape_frontend.xcodeproj/project.pbxproj @@ -190,6 +190,10 @@ 67E91C7E1BDFC85E005CEE88 /* visual_params.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6709478F1BDF9BE1005014C0 /* visual_params.cpp */; }; BB035F6F1E3A2AAE00519962 /* drape_measurer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB035F6D1E3A2AAE00519962 /* drape_measurer.cpp */; }; BB035F701E3A2AAE00519962 /* drape_measurer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = BB035F6E1E3A2AAE00519962 /* drape_measurer.hpp */; }; + BB59CED520BF6E46008A9ADF /* transit_scheme_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB59CED120BF6E46008A9ADF /* transit_scheme_builder.cpp */; }; + BB59CED620BF6E46008A9ADF /* transit_scheme_renderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB59CED220BF6E46008A9ADF /* transit_scheme_renderer.cpp */; }; + BB59CED720BF6E46008A9ADF /* transit_scheme_renderer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = BB59CED320BF6E46008A9ADF /* transit_scheme_renderer.hpp */; }; + BB59CED820BF6E46008A9ADF /* transit_scheme_builder.hpp in Headers */ = {isa = PBXBuildFile; fileRef = BB59CED420BF6E46008A9ADF /* transit_scheme_builder.hpp */; }; BB7D67D21F34A62C002FD122 /* custom_features_context.hpp in Headers */ = {isa = PBXBuildFile; fileRef = BB7D67CF1F34A62C002FD122 /* custom_features_context.hpp */; }; BB7D67D31F34A62C002FD122 /* path_text_handle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BB7D67D01F34A62C002FD122 /* path_text_handle.cpp */; }; BB7D67D41F34A62C002FD122 /* path_text_handle.hpp in Headers */ = {isa = PBXBuildFile; fileRef = BB7D67D11F34A62C002FD122 /* path_text_handle.hpp */; }; @@ -459,6 +463,10 @@ 677A2DE41C0DD55D00635A00 /* requested_tiles.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = requested_tiles.hpp; sourceTree = ""; }; BB035F6D1E3A2AAE00519962 /* drape_measurer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = drape_measurer.cpp; sourceTree = ""; }; BB035F6E1E3A2AAE00519962 /* drape_measurer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = drape_measurer.hpp; sourceTree = ""; }; + BB59CED120BF6E46008A9ADF /* transit_scheme_builder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = transit_scheme_builder.cpp; sourceTree = ""; }; + BB59CED220BF6E46008A9ADF /* transit_scheme_renderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = transit_scheme_renderer.cpp; sourceTree = ""; }; + BB59CED320BF6E46008A9ADF /* transit_scheme_renderer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = transit_scheme_renderer.hpp; sourceTree = ""; }; + BB59CED420BF6E46008A9ADF /* transit_scheme_builder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = transit_scheme_builder.hpp; sourceTree = ""; }; BB7D67CF1F34A62C002FD122 /* custom_features_context.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = custom_features_context.hpp; sourceTree = ""; }; BB7D67D01F34A62C002FD122 /* path_text_handle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = path_text_handle.cpp; sourceTree = ""; }; BB7D67D11F34A62C002FD122 /* path_text_handle.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = path_text_handle.hpp; sourceTree = ""; }; @@ -579,6 +587,10 @@ 670947411BDF9B99005014C0 /* drape_frontend */ = { isa = PBXGroup; children = ( + BB59CED120BF6E46008A9ADF /* transit_scheme_builder.cpp */, + BB59CED420BF6E46008A9ADF /* transit_scheme_builder.hpp */, + BB59CED220BF6E46008A9ADF /* transit_scheme_renderer.cpp */, + BB59CED320BF6E46008A9ADF /* transit_scheme_renderer.hpp */, 454B9A391F4591AC003FAE7A /* drape_engine_safe_ptr.hpp */, 453FEDAA1F34C257005C1BB4 /* render_state.cpp */, 453FEDAB1F34C257005C1BB4 /* render_state.hpp */, @@ -868,6 +880,7 @@ 670948171BDF9C39005014C0 /* interpolation_holder.hpp in Headers */, 45BB025E1EB8BE5200FE5C0C /* shader_def.hpp in Headers */, 670947FD1BDF9BF5005014C0 /* base_renderer.hpp in Headers */, + BB59CED820BF6E46008A9ADF /* transit_scheme_builder.hpp in Headers */, 670947D51BDF9BE1005014C0 /* tile_utils.hpp in Headers */, 670947D91BDF9BE1005014C0 /* user_mark_shapes.hpp in Headers */, 452C9EDD1CEDCF3200A55E57 /* linear_animation.hpp in Headers */, @@ -877,6 +890,7 @@ 670947931BDF9BE1005014C0 /* kinetic_scroller.hpp in Headers */, 670947E71BDF9BEC005014C0 /* frontend_renderer.hpp in Headers */, 670947F71BDF9BF5005014C0 /* apply_feature_functors.hpp in Headers */, + BB59CED720BF6E46008A9ADF /* transit_scheme_renderer.hpp in Headers */, 670947CD1BDF9BE1005014C0 /* tile_info.hpp in Headers */, 670947C31BDF9BE1005014C0 /* stylist.hpp in Headers */, 670948481BDF9C48005014C0 /* layer_render.hpp in Headers */, @@ -1045,6 +1059,7 @@ 670947941BDF9BE1005014C0 /* line_shape_helper.cpp in Sources */, 670947C21BDF9BE1005014C0 /* stylist.cpp in Sources */, 670947AD1BDF9BE1005014C0 /* poi_symbol_shape.cpp in Sources */, + BB59CED620BF6E46008A9ADF /* transit_scheme_renderer.cpp in Sources */, 6709479F1BDF9BE1005014C0 /* message_queue.cpp in Sources */, 452C9EE41CEDCF3200A55E57 /* sequence_animation.cpp in Sources */, BB7D67D31F34A62C002FD122 /* path_text_handle.cpp in Sources */, @@ -1068,6 +1083,7 @@ 670948021BDF9BF5005014C0 /* drape_engine.cpp in Sources */, 56D545661C74A44900E3719C /* overlay_batcher.cpp in Sources */, 672D249A1E892768004BB7B1 /* overlays_tracker.cpp in Sources */, + BB59CED520BF6E46008A9ADF /* transit_scheme_builder.cpp in Sources */, 452C9ED81CEDCF3200A55E57 /* follow_animation.cpp in Sources */, 6709481C1BDF9C39005014C0 /* opacity_animation.cpp in Sources */, 452FE5C31EB2004F00EE470C /* screen_quad_renderer.cpp in Sources */,