Display: none on Deck.gl React component causes huge performance issue
Asked Answered
K

2

7

EDIT: This issue is specific to this setup. Please use the code from here if you wish to help. Thanks!

I am using Deck.gl with react to display a map. When I try to hide the map with display: none it starts to freeze up my whole computer. I have worked around this by using visibility: collapse instead, but I would like to know why display: none causes this problem.

The console starts perpetually filling up with warnings:

luma: Device pixel ratio clamped context.js:202:84
Error: WebGL warning: clear: Requested size 21535x8218 was too large, but resize to 10767x4109 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawElements: Drawing to a destination rect smaller than the viewport rect. (This warning will only be given once) bundle.min.js line 15214 > eval:32:191695
Error: WebGL warning: drawingBufferWidth: Requested size 21535x8218 was too large, but resize to 10767x4109 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 20020x7637 was too large, but resize to 10010x3818 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 20020x7637 was too large, but resize to 10010x3818 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 18609x7099 was too large, but resize to 9304x3549 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 18609x7099 was too large, but resize to 9304x3549 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 17297x6598 was too large, but resize to 8648x3299 succeeded. bundle.min.js line 15214 > eval:32:60490
Source map error: Error: NetworkError when attempting to fetch resource.
Resource URL: webpack:///./node_modules/@luma.gl/webgl/dist/esm/context/context.js?
Source Map URL: context.js.map
Error: WebGL warning: drawingBufferWidth: Requested size 17297x6598 was too large, but resize to 8648x3299 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 21925x8361 was too large, but resize to 10962x4180 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 21925x8361 was too large, but resize to 10962x4180 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 20379x7772 was too large, but resize to 10189x3886 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 20379x7772 was too large, but resize to 10189x3886 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 18943x7225 was too large, but resize to 9471x3612 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 18943x7225 was too large, but resize to 9471x3612 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 17607x6715 was too large, but resize to 8803x3357 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 17607x6715 was too large, but resize to 8803x3357 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 22314x8510 was too large, but resize to 11157x4255 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 22314x8510 was too large, but resize to 11157x4255 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 20743x7911 was too large, but resize to 10371x3955 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 20743x7911 was too large, but resize to 10371x3955 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 19280x7354 was too large, but resize to 9640x3677 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 19280x7354 was too large, but resize to 9640x3677 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 17925x6834 was too large, but resize to 8962x3417 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 17925x6834 was too large, but resize to 8962x3417 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 16662x6351 was too large, but resize to 8331x3175 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 16662x6351 was too large, but resize to 8331x3175 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 21115x8049 was too large, but resize to 10557x4024 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 21115x8049 was too large, but resize to 10557x4024 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 19626x7482 was too large, but resize to 9813x3741 succeeded. bundle.min.js line 15214 > eval:32:60490
Error: WebGL warning: drawingBufferWidth: Requested size 19626x7482 was too large, but resize to 9813x3741 succeeded. bundle.min.js line 7141 > eval:201:9
Error: WebGL warning: clear: Requested size 18246x6954 was too large, but resize to 9123x3477 succeeded. bundle.min.js line 15214 > eval:32:60490

I took a look at performance with the React profiler: React Profiler output

In every commit that takes longer to process than normal, it shows AutoSizer taking a long time to render.

Reproducing the problem:

Node version: v10.16.3

  1. Run create-react-app my-app.
  2. Copy code below.
  3. Apply display: none on any element containing the map, including the elements rendered by Deck.gl.

WARNING: Reproducing this issue takes up a lot of resources and might crash your computer!

The code

index.tsx

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as serviceWorker from './serviceWorker';
import 'mapbox-gl/dist/mapbox-gl.css';
import {GeoMapChart} from './GeoMapChart'

ReactDOM.render(<GeoMapChart />, document.getElementById('root'));

serviceWorker.unregister();

GeoMapChart.tsx

import * as React from 'react';
import DeckGL from '@deck.gl/react';
import {StaticMap} from 'react-map-gl';


export function GeoMapChart() {

    const [viewState, setViewState] = React.useState(INITIAL_VIEW_STATE);

    return (
            <DeckGL viewState={viewState} controller={true} width={'100%'} height={'100%'} layers={[]}  onViewStateChange={
                ({viewState, oldViewState, interactionState}) => {
                    const newViewState = {...viewState};
                    setViewState(newViewState);
                }}>

                <StaticMap width={'100%'} height={'100%'} mapStyle={MAPBOX_BASE_LAYER}/>
            </DeckGL>
    )
}

const MAX_ZOOM = 19;
const MIN_ZOOM = 2;
const INITIAL_VIEW_STATE = {
    latitude: 37.77,
    longitude: -122.42,
    zoom: 5,
    bearing: 0,
    pitch: 0,
    maxZoom: MAX_ZOOM,
    minZoom: MIN_ZOOM
};


const BASEMAP_TILE_SOURCE_NAME = 'simple-tiles';
const BASEMAP_TILE_SERVERS = [
    'http://a.tile.osm.org/{z}/{x}/{y}.png',
    'http://b.tile.osm.org/{z}/{x}/{y}.png',
    'http://c.tile.osm.org/{z}/{x}/{y}.png',
    //'//stamen-tiles-a.a.ssl.fastly.net/terrain/{z}/{x}/{y}.jpg',
    //'//stamen-tiles-b.a.ssl.fastly.net/terrain/{z}/{x}/{y}.jpg',
    //'//stamen-tiles-c.a.ssl.fastly.net/terrain/{z}/{x}/{y}.jpg',
];
const BASEMAP_ATTRIBUTION = `Map tiles by <a href="http://stamen.com">Stamen Design</a>, under
<a href="http://creativecommons.org/licenses/by/3.0"> CC BY 3.0</a>. Data by
<a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">
ODbL</a>.`.replace(/\n/gm, '');
const COMMON_LAYER_CONFIG = {
    minZoom: 2,
    maxZoom: 17, // New data will be requested until this level
    pixelScaleFactor: 8,
    tileSize: 256,
    isTms: true,
    topoLayerClusteringSwithLevel: 13,
    maxVisibleRasterLayers: 3,
    maxConfigurableLayers: 26,
};
const MAP_CONFIG = {
    MIN_ZOOM: 1,
    MAX_ZOOM: 18,
    INITIAL_ZOOM: 9,
    SHOW_TILE_BOUNDARIES: false,
    DRAG_ROTATE: false,
    ZOOM_NO_DATA: 2,
    SEARCH_DEFAULT_ZOOM: 14,
};

const MAPBOX_BASE_LAYER = {
    version: 8,
    sources: {
        [BASEMAP_TILE_SOURCE_NAME]: {
            type: 'raster',
            tiles: BASEMAP_TILE_SERVERS,
            tileSize: COMMON_LAYER_CONFIG.tileSize,
            attribution: BASEMAP_ATTRIBUTION,
        }
    },
    layers: [
        {
            id: BASEMAP_TILE_SOURCE_NAME,
            type: 'raster',
            source: BASEMAP_TILE_SOURCE_NAME,
            minzoom: MAP_CONFIG.MIN_ZOOM,
            maxzoom: MAP_CONFIG.MAX_ZOOM,
        }
    ],
};

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />

  <title>Deck.gl performance issue</title>
</head>

<body>
  <div id="root"></div>

</body>

</html>

package.json

{
    "name": "NOT_PUBLIC",
    "version": "0.1.0",
    "private": true,
    "dependencies": {
        "@types/lodash": "^4.14.149",
        "@types/node": "13.1.8",
        "@types/react": "16.8.3",
        "@types/react-dom": "16.0.11",
        "@types/react-router-dom": "5.1.3",
        "axios": "0.19.0",
        "deck.gl": "7.3.7",
        "jss": "10.0.0-alpha.3",
        "lodash": "4.17.15",
        "react": "16.8.5",
        "react-dom": "16.8.5",
        "react-map-gl": "5.1.3",
        "react-router-dom": "5.1.2"
    },
    "scripts": {
        "start": "webpack-dev-server --mode development --hot",
        "build": "webpack --mode production",
        "test": "react-scripts test",
        "eject": "react-scripts eject"
    },
    "eslintConfig": {
        "extends": "react-app"
    },
    "browserslist": [
        ">0.2%",
        "not dead",
        "not ie <= 11",
        "not op_mini all"
    ],
    "devDependencies": {
        "@danmarshall/deckgl-typings": "^3.4.3",
        "@types/react-map-gl": "^5.0.3",
        "@types/uuid": "^3.4.6",
        "awesome-typescript-loader": "^5.2.1",
        "babel-jest": "^23.6.0",
        "css-loader": "^2.1.0",
        "enzyme": "^3.8.0",
        "enzyme-adapter-react-16": "^1.7.1",
        "enzyme-to-json": "^3.3.5",
        "html-loader": "^0.5.5",
        "html-webpack-plugin": "^3.2.0",
        "jest": "^23.6.0",
        "node-sass": "^4.11.0",
        "react-scripts": "2.1.2",
        "react-test-renderer": "^16.7.0",
        "sass-loader": "^7.1.0",
        "source-map-loader": "^0.2.4",
        "style-loader": "^0.23.1",
        "typescript": "3.7.4",
        "webpack": "^4.28.1",
        "webpack-cli": "^3.2.1",
        "webpack-dev-server": "^3.1.14"
    },
    "jest": {
        "snapshotSerializers": [
            "enzyme-to-json/serializer"
        ]
    }
}

webpack.config.js

const path = require('path');

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.tsx',
  devServer: {
    port: 9012,
    historyApiFallback: true,
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js']
  },
  output: {
    path: path.join(__dirname, '/dist'),
    filename: 'bundle.min.js'
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'awesome-typescript-loader'
      },
      {
        test:/\.css$/,
        use:['style-loader','css-loader', 'sass-loader']
      },
      {
        test: /\.scss$/, 
        use:["css-loader",'sass-loader']
      },
      {
        test: /\.(?:png|jpg|svg)$/,
        loader: 'url-loader'

    },
    {
      test: /\.(ico|jpeg|gif|eot|otf|webp|ttf|woff|woff2)(\?.*)?$/,
      loader: 'file-loader'

  },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: '!!html-loader!./src/index.html'
    }),

  ]
}

tsconfig.json

{
    "compilerOptions": {
        "outDir": "./dist/",
        "sourceMap": true,
        "noImplicitAny": false,
        "module": "commonjs",
        "target": "es6",
        "jsx": "react",
        // "suppressImplicitAnyIndexErrors": true,
    },
    "include": [
        "./src/**/*"
    ]
}
Klug answered 10/2, 2020 at 8:21 Comment(10)
Can't help you debug if you don't post the a minimal reproducable example in the question itself (not a link offsite)Dulla
Okay, I'll try to isolate it and update the post.Klug
Do you apply display: none only via DevTools or you do it programmatically?Gorgonian
When I found the problem I was applying it through CSS. But it does not matter how it is being applied (CSS, JS, devtool), the issue still comes up.Klug
@Vladimir SerykhKlug
I tried to reproduce the bug but with no success. There is my github github.com/klishevich/stackoverflow-webgl and this is the very commit adding display:none and it works - github.com/klishevich/stackoverflow-webgl/commit/…Orfield
@MichaelKlishevich I have ran your code and the issue happens for me. I have tried upgrading my node version(v12.16.0), but the issue persisted.Klug
@LeventeRozsenich can you try with different browser ?Tiphane
I can confirm it really takes a lot of time to renderTiphane
@TripurariShankar I have tried it with Firefox and Chrome. Issue remains in both.Klug
K
2

Upgrading all my dependencies to the latest version solved my issue. Sorry but I couldn't be bothered to pinpoint which package was causing the issue. Maybe I'll spend the time on it later in life. Also on [email protected] compiler option skipLibCheck: true is needed.

Node version 12.16.0

package.json:

{
    "name": "NOT_PUBLIC",
    "version": "0.1.0",
    "private": true,
    "dependencies": {
        "@eds/vanilla": "3.4.0",
        "@types/lodash": "^4.14.149",
        "@types/node": "13.7.4",
        "@types/react": "16.9.20",
        "@types/react-dom": "16.9.5",
        "@types/react-redux": "7.1.7",
        "@types/react-router-dom": "5.1.3",
        "axios": "0.19.2",
        "d3": "5.15.0",
        "deck.gl": "^8.0.15",
        "dragula": "3.7.2",
        "immer": "5.3.6",
        "jss": "10.0.4",
        "lodash": "4.17.15",
        "react": "16.12.0",
        "react-dom": "16.12.0",
        "react-map-gl": "5.2.3",
        "react-redux": "7.2.0",
        "react-router-dom": "5.1.2",
        "redux": "4.0.5",
        "redux-thunk": "2.3.0"
    },
    "scripts": {
        "start": "webpack-dev-server --mode development --open --hot",
        "build": "webpack --mode production",
        "test": "react-scripts test",
        "eject": "react-scripts eject"
    },
    "eslintConfig": {
        "extends": "react-app"
    },
    "browserslist": [
        ">0.2%",
        "not dead",
        "not ie <= 11",
        "not op_mini all"
    ],
    "devDependencies": {
        "@types/react-map-gl": "^5.2.0",
        "@types/uuid": "^3.4.7",
        "awesome-typescript-loader": "^5.2.1",
        "babel-jest": "^25.1.0",
        "css-loader": "^3.4.2",
        "enzyme": "^3.11.0",
        "enzyme-adapter-react-16": "^1.15.2",
        "enzyme-to-json": "^3.4.4",
        "html-loader": "^0.5.5",
        "html-webpack-plugin": "^3.2.0",
        "jest": "^25.1.0",
        "node-sass": "^4.13.1",
        "react-scripts": "3.4.0",
        "react-test-renderer": "^16.12.0",
        "sass-loader": "^8.0.2",
        "source-map-loader": "^0.2.4",
        "style-loader": "^1.1.3",
        "typescript": "3.7.5",
        "webpack": "^4.41.6",
        "webpack-cli": "^3.3.11",
        "webpack-dev-server": "^3.10.3"
    },
    "jest": {
        "snapshotSerializers": [
            "enzyme-to-json/serializer"
        ]
    }
}
Klug answered 19/2, 2020 at 21:51 Comment(0)
P
-1

I will try to clear the confusion here. Maybe it answers your questions.

You have tried to set the div hidden using display: none and it uses a lot much more memory than it used while display: block or setting the style to visibility: collapse. I think this as the problem statement.

Below are the options to hide an element in html:-

  • opacity: 0
  • visibility: hidden
  • visibility: collapse
  • display: none

Now, the actual thing these styles do html is below

  • opacity: 0 - hides an element, but it does occupy space in the layout, can click on the element.
  • visibility: hidden - hides an element, but it does occupy space in the layout, no clicks possible.
  • visibility: collapse - hides an element, but it does occupy space in the layout, no clicks possible. (it does not occupy space in the layout if the element is a table)
  • display: none - hides an element entirely, it doesn't occupy any space in the layout, no clicks possible.

From this comparison, we can understand that display: none; doesn't allow the element to occupy the space, so the attributes clientHeight and clientWidth of the div will be zero. The following are just my assumptions.

As this turned out to be, the width and height marked as "100%" might have resulted in zero. But the library set/reset this height and width to some higher number, probably based on some validations and screen height and screen width (I am not sure, but in my system, it was 8144x4072 and yours seems to be 9123x3477). Also, the library seems to be caching canvas values during the render process. So these calculations and caching might be expensive in terms of memory and CPU.

so, these might have (You can imagine storing an array of size 8144x4072) caused the memory issues, especially when the audit is going on the RAM will get filled up easily and caused the issues.

Hope this gives you some clarity.

Pollinize answered 17/2, 2020 at 16:27 Comment(1)
Thank you for the effort. I understand that once the height of the containing element is set to zero, something inside Deck.gl will start perpetually resizing itself and that is a huge hit on the machine. I confirmed that this is not an issue if I create a new react project and add Deck.gl. This issue is specific to this project. I'll start upgrading the versions of my dependencies and see if that works.Klug

© 2022 - 2024 — McMap. All rights reserved.