mapbox / mapbox-gl-js

Interactive, thoroughly customizable maps in the browser, powered by vector tiles and WebGL
https://docs.mapbox.com/mapbox-gl-js/
Other
11.26k stars 2.23k forks source link

Workaround unexpected terrain spikes in browsers with fingerprint protection enabled #12997

Open astojilj opened 1 year ago

astojilj commented 1 year ago

The v3 migration guide Known issues and limitations refers to an issue with spikes visible on terrain when run in Safari 17 private browsing mode (edit: and Firefox with fingerprint protection enabled):

In Safari 17 private browsing mode, Apple's Advanced Privacy Protection introduces noise into key fingerprinting areas like 2D Canvas and WebGL and may cause unexpected terrain spikes in GL JS v3.

The issue also exists with previous GL JS versions.

While we're working on resolving this, a feasible workaround would be to disable terrain from the application code, when it is detected that anti-fingerprinting protection introduces noise to 2D canvas operations:

export function isPrivateModeSafariWithCanvasFingerprintProtection(scope: any): boolean {
    if (!isSafari(scope)) return false;
    // Starting from version 17, fingerprinting protection introduces fuzz to Canvas2D operations used for image decoding.
    const version = scope.navigator ? scope.navigator.userAgent.match(/Version\/([0-9\._]+).*Safari/) : [];
    if (!(version.length === 2 && parseInt(version[1]) >= 17)) {
        return false;
    }
    assert(window.OffscreenCanvas);
    const offscreenCanvas = new window.OffscreenCanvas(255 / 3, 1);
    const offscreenCanvasContext = offscreenCanvas.getContext('2d', {willReadFrequently: true});
    let inc = 0;
    // getImageData is lossy with premultiplied alpha.
    for (let i = 0; i < offscreenCanvas.width; ++i) {
        offscreenCanvasContext.fillStyle = `rgba(${inc++},${inc++},${inc++}, 255)`;
        offscreenCanvasContext.fillRect(i, 0, 1, 1);
    }
    const readData = offscreenCanvasContext.getImageData(0, 0, offscreenCanvas.width, offscreenCanvas.height);
    inc = 0;
    for (let i = 0; i < readData.data.length; ++i) {
        if (i % 4 !== 3) {
            if (inc++ !== readData.data[i]) {
                return true;
            }
        }
    }
    return false;
}

image

CptHolzschnauz commented 10 months ago

Same here with Firefox 121.0.1 64bit on Mac OS, with every version of old Mapbox GL JS maps i tried, IMHO a issue with the changed renderer, not with Mapbox.

stepankuzmin commented 10 months ago

We'll ship this workaround along with the v3.1 release.

slavanga commented 8 months ago

@stepankuzmin Seeing a visually different, but probably related rendering bug only in Safari private mode and Brave. The issue appears with both v3.1.0 and v3.2.0.

Here's a minimal codepen based on the globe example from the docs. Just the map style was replaced with one where 3D terrain is enabled: https://codepen.io/slavanga/pen/eYovgLg

Bildschirmfoto 2024-03-20 um 17 37 03

Rendering with the default monochrome style, just 3D terrain enabled:

Bildschirmfoto 2024-03-20 um 17 38 18

Video of the same:

https://github.com/mapbox/mapbox-gl-js/assets/1077807/caba5574-a6c2-4b96-b289-70d522fb5db1

Rendering with a more customized map style:

Bildschirmfoto 2024-03-20 um 17 26 55
calb-dev commented 8 months ago

Also seeing the same thing as @slavanga on firefox 124.0.1 Windows Screenshot 2024-03-22 180556

stepankuzmin commented 8 months ago

Hi @slavanga and @calb-dev,

Thanks for flagging this, I confirm this is still an issue. The fix for the workaround is coming in v3.3.0.

elygeo commented 5 months ago

It seems the workaround disables terrain when configured in a style, but not for map.setTarrain. For example, this still displays with noisy terrain:

https://docs.mapbox.com/mapbox-gl-js/example/add-terrain/

Can you recommend a similar workaround for disabling hillshade? It also has the noise issue here:

https://docs.mapbox.com/mapbox-gl-js/example/hillshade/

Maybe make use of hasCanvasFingerprintNoise() in src/util/browser.ts?

CptHolzschnauz commented 5 months ago

The issue is back with 3.5.1 in Firefox:

Bildschirmfoto 2024-07-05 um 16 35 06

While in 3.4 the problem was solved:

Bildschirmfoto 2024-07-05 um 16 38 43
stepankuzmin commented 4 months ago

Thanks for flagging this again. The fix for the workaround is coming in v3.5.2.

CptHolzschnauz commented 4 months ago

Issue is still present with 3.52 when just adding a layer of type symbol. Even worse with Safari on iOS (Screenshot below). IMHO this makes MapBox unusable for PWAs. Error is not occuring with MapBox 3.4 or MapLibre. IMG_8925

stepankuzmin commented 4 months ago

Yeah, I can reproduce it now, good catch @CptHolzschnauz. We'll fix that in the next release.

CptHolzschnauz commented 4 months ago

Thx. Let me know if I can help you with providing informations. My experience is that sometimes the bug occurs right from the initial render and sometimes you have to zoom out until it starts and getting worse until mapbox JS crashes.

CptHolzschnauz commented 4 months ago

It's occuring when tilting, see the video below. iOS 17.5.1 with Safari.

https://github.com/user-attachments/assets/d82129f7-5c86-4a50-8507-45f53bfc4bff