fleaflet / flutter_map

A versatile mapping package for Flutter. Simple and easy to learn, yet completely customizable and configurable, it's the best choice for mapping in your Flutter app.
https://pub.dev/packages/flutter_map
BSD 3-Clause "New" or "Revised" License
2.68k stars 848 forks source link

[BUG] Polygon simplification triggers crash on iOS in 7.0 #1903

Closed Elleo closed 4 weeks ago

Elleo commented 1 month ago

What is the bug?

When a polygon gets simplified beyond a certain point, by the user zooming out, the application crashes. This only appears to happen on iOS.

As far as I can tell the polygon needs to have a hole in it to trigger the crash.

Here's the backtrace from the crash:

  * frame #0: 0x000000010359fc9c Flutter`impeller::Tessellator::TessellateConvex(impeller::Path const&, float) + 1092
    frame #1: 0x000000010374cf24 Flutter`impeller::FillPathGeometry::GetPositionBuffer(impeller::ContentContext const&, impeller::Entity const&, impeller::RenderPass&) const + 248
    frame #2: 0x000000010373bbcc Flutter`impeller::SolidColorContents::Render(impeller::ContentContext const&, impeller::Entity const&, impeller::RenderPass&) const + 948
    frame #3: 0x000000010374b770 Flutter`impeller::EntityPass::RenderElement(impeller::Entity&, unsigned long, impeller::InlinePassContext&, int, impeller::ContentContext&, impeller::EntityPassClipStack&, impeller::TPoint<float>) const + 2056
    frame #4: 0x000000010374a074 Flutter`impeller::EntityPass::OnRender(impeller::ContentContext&, impeller::Capture&, impeller::TSize<long long>, impeller::EntityPassTarget&, impeller::TPoint<float>, impeller::TPoint<float>, unsigned int, impeller::EntityPassClipStack&, unsigned long, std::_fl::shared_ptr<impeller::Contents>, std::_fl::optional<impeller::InlinePassContext::RenderPassResult> const&) const + 5392
    frame #5: 0x00000001036e9224 Flutter`impeller::AiksContext::Render(impeller::Picture const&, impeller::RenderTarget&, bool) + 4468
    frame #6: 0x000000010359e344 Flutter`impeller::Renderer::Render(std::_fl::unique_ptr<impeller::Surface, std::_fl::default_delete<impeller::Surface>>, std::_fl::function<bool (impeller::RenderTarget&)> const&) const + 340
    frame #7: 0x00000001038088c4 Flutter`std::_fl::__function::__func<fml::internal::CopyableLambda<flutter::GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLayer(SkISize const&)::$_0>, std::_fl::allocator<fml::internal::CopyableLambda<flutter::GPUSurfaceMetalImpeller::AcquireFrameFromCAMetalLayer(SkISize const&)::$_0>>, bool (flutter::SurfaceFrame&, flutter::DlCanvas*)>::operator()(flutter::SurfaceFrame&, flutter::DlCanvas*&&) + 916
    frame #8: 0x00000001036e7458 Flutter`flutter::SurfaceFrame::Submit() + 92
    frame #9: 0x000000010361f4ac Flutter`flutter::Rasterizer::DrawToSurfacesUnsafe(flutter::FrameTimingsRecorder&, std::_fl::vector<std::_fl::unique_ptr<flutter::LayerTreeTask, std::_fl::default_delete<flutter::LayerTreeTask>>, std::_fl::allocator<std::_fl::unique_ptr<flutter::LayerTreeTask, std::_fl::default_delete<flutter::LayerTreeTask>>>>) + 1832
    frame #10: 0x0000000103620244 Flutter`std::_fl::__function::__func<flutter::Rasterizer::DrawToSurfaces(flutter::FrameTimingsRecorder&, std::_fl::vector<std::_fl::unique_ptr<flutter::LayerTreeTask, std::_fl::default_delete<flutter::LayerTreeTask>>, std::_fl::allocator<std::_fl::unique_ptr<flutter::LayerTreeTask, std::_fl::default_delete<flutter::LayerTreeTask>>>>)::$_1, std::_fl::allocator<flutter::Rasterizer::DrawToSurfaces(flutter::FrameTimingsRecorder&, std::_fl::vector<std::_fl::unique_ptr<flutter::LayerTreeTask, std::_fl::default_delete<flutter::LayerTreeTask>>, std::_fl::allocator<std::_fl::unique_ptr<flutter::LayerTreeTask, std::_fl::default_delete<flutter::LayerTreeTask>>>>)::$_1>, void ()>::operator()() + 64
    frame #11: 0x0000000103516d34 Flutter`fml::SyncSwitch::Execute(fml::SyncSwitch::Handlers const&) const + 72
    frame #12: 0x000000010361ebc4 Flutter`flutter::Rasterizer::DrawToSurfaces(flutter::FrameTimingsRecorder&, std::_fl::vector<std::_fl::unique_ptr<flutter::LayerTreeTask, std::_fl::default_delete<flutter::LayerTreeTask>>, std::_fl::allocator<std::_fl::unique_ptr<flutter::LayerTreeTask, std::_fl::default_delete<flutter::LayerTreeTask>>>>) + 364
    frame #13: 0x000000010362110c Flutter`std::_fl::__function::__func<flutter::Rasterizer::Draw(std::_fl::shared_ptr<flutter::Pipeline<flutter::FrameItem>> const&)::$_0, std::_fl::allocator<flutter::Rasterizer::Draw(std::_fl::shared_ptr<flutter::Pipeline<flutter::FrameItem>> const&)::$_0>, void (std::_fl::unique_ptr<flutter::FrameItem, std::_fl::default_delete<flutter::FrameItem>>)>::operator()(std::_fl::unique_ptr<flutter::FrameItem, std::_fl::default_delete<flutter::FrameItem>>&&) + 208
    frame #14: 0x00000001036208f4 Flutter`flutter::Rasterizer::Draw(std::_fl::shared_ptr<flutter::Pipeline<flutter::FrameItem>> const&) + 452
    frame #15: 0x000000010363b2a8 Flutter`std::_fl::__function::__func<fml::internal::CopyableLambda<flutter::Shell::OnAnimatorDraw(std::_fl::shared_ptr<flutter::Pipeline<flutter::FrameItem>>)::$_0>, std::_fl::allocator<fml::internal::CopyableLambda<flutter::Shell::OnAnimatorDraw(std::_fl::shared_ptr<flutter::Pipeline<flutter::FrameItem>>)::$_0>>, void ()>::operator()() + 168
    frame #16: 0x00000001035152f0 Flutter`fml::MessageLoopImpl::FlushTasks(fml::FlushType) + 592
    frame #17: 0x000000010351940c Flutter`fml::MessageLoopDarwin::OnTimerFire(__CFRunLoopTimer*, fml::MessageLoopDarwin*) + 32
    frame #18: 0x00000001af6f1bb0 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 28
    frame #19: 0x00000001af6b2de4 CoreFoundation`__CFRunLoopDoTimer + 884
    frame #20: 0x00000001af65c0fc CoreFoundation`__CFRunLoopDoTimers + 284
    frame #21: 0x00000001af6a55bc CoreFoundation`__CFRunLoopRun + 1940
    frame #22: 0x00000001af6a9d20 CoreFoundation`CFRunLoopRunSpecific + 584
    frame #23: 0x00000001035194f8 Flutter`fml::MessageLoopDarwin::Run() + 88
    frame #24: 0x000000010351859c Flutter`std::_fl::__function::__func<fml::Thread::Thread(std::_fl::function<void (fml::Thread::ThreadConfig const&)> const&, fml::Thread::ThreadConfig const&)::$_0, std::_fl::allocator<fml::Thread::Thread(std::_fl::function<void (fml::Thread::ThreadConfig const&)> const&, fml::Thread::ThreadConfig const&)::$_0>, void ()>::operator()() + 180
    frame #25: 0x00000001035182ac Flutter`fml::ThreadHandle::ThreadHandle(std::_fl::function<void ()>&&)::$_0::__invoke(void*) + 36
    frame #26: 0x00000001fa3dc0ec libsystem_pthread.dylib`_pthread_start + 116

How can we reproduce it?

The issue can be reproduced by running the following code on an iOS device, the crash should happen immediately:

          FlutterMap(
            options: const MapOptions(
              initialCenter: LatLng(62.052959, -2.06089),
              initialZoom: 5,
            ),
            children: [
              PolygonLayer(
                polygons: [
                  Polygon(
                      points: [
                        LatLng(62.05508, -2.065901),
                        LatLng(62.054241, -2.060394),
                        LatLng(62.053185, -2.060789),
                        LatLng(62.052959, -2.06089),
                        LatLng(62.052807, -2.061024),
                        LatLng(62.05246, -2.061067),
                        LatLng(62.050884, -2.061689),
                        LatLng(62.050755, -2.061773),
                        LatLng(62.051334, -2.065968),
                        LatLng(62.051392, -2.066128),
                        LatLng(62.051704, -2.066019),
                        LatLng(62.0518, -2.066044),
                        LatLng(62.05191, -2.06622),
                        LatLng(62.051994, -2.066153),
                        LatLng(62.052216, -2.06617),
                        LatLng(62.052147, -2.066901),
                        LatLng(62.05307, -2.066641),
                        LatLng(62.054859, -2.066229),
                        LatLng(62.055008, -2.066119),
                        LatLng(62.05508, -2.065901)
                      ],
                      holePointsList: [
                        [
                          LatLng(62.052925, -2.062824),
                          LatLng(62.052868, -2.062975),
                          LatLng(62.052742, -2.062967),
                          LatLng(62.052635, -2.062899),
                          LatLng(62.05249, -2.062613),
                          LatLng(62.05239, -2.062049),
                          LatLng(62.052383, -2.061845),
                          LatLng(62.052505, -2.061613),
                          LatLng(62.05257, -2.061588),
                          LatLng(62.052643, -2.061579),
                          LatLng(62.052727, -2.061697),
                          LatLng(62.052818, -2.061975),
                          LatLng(62.052948, -2.062588),
                          LatLng(62.052925, -2.062824)
                        ]
                      ],
                      color: Colors.lightBlue.withOpacity(0.5),
                      borderColor: Colors.lightBlue,
                      borderStrokeWidth: 1),
                ],
              ),
            ],
          ),

I was able to reproduce this on both a physical iPhone 8 running iOS 16.7.8 and using the iOS simulator, with an iPhone 15 running iOS 17.5

I wasn't able to reproduce this on Android or Windows, only on iOS.

Do you have a potential solution?

No solution, but it can be temporarily worked around by either setting the simplificationThreshold property on the PolygonLayer to 0, or setting the useAltRendering property to true

Platforms

iOS

Severity

Fatal: Causes the application to crash

JaffaKetchup commented 1 month ago

Hi @Elleo, This looks like an Impeller issue. We would hugely appreciate it if you could open an issue in the Flutter repo! (FM is kinda well known there for finding Impeller bugs, we've/the community has found 3 already). Please link back here if you can! If not, we're happy to open it, just let us know :)

Elleo commented 1 month ago

Hi @JaffaKetchup,

I think it might warrant investigation on the flutter_map side first, as I've found similar issues already logged against the Flutter repo, which have been closed by the Flutter team under the assumption that the actual crash is being triggered elsewhere (but appearing to be in impeller due to the bad memory access), e.g. https://github.com/flutter/flutter/issues/125749

If you think it's still worth opening one straight away I'm happy to do so though :)

Thanks! Mike

JaffaKetchup commented 1 month ago

I'm pretty sure this is a Flutter issue, and I'll be willing to defend that. They say the issue with memory issues is any "plugin" can cause that - we are not a plugin. We use Flutter and only Flutter. If it worked (or at least didn't crash) with Skia and now it doesn't with Impeller, that seems to suggest an Impeller issue. Still happy to do it if you'd rather I did it!

Elleo commented 1 month ago

Okay, cool; l'll upload a full repository with an example tomorrow to make it easier for them to verify and file a bug

Thanks for taking a look!

JaffaKetchup commented 1 month ago

Thanks, that's very helpful :)

Elleo commented 1 month ago

Logged against Flutter here: https://github.com/flutter/flutter/issues/149646

JaffaKetchup commented 4 weeks ago

Thanks @Elleo :) This is fixed by https://github.com/flutter/engine/pull/52401. We have to wait for this to land. I've requested a cherry-pick.