playcanvas / editor

Issue tracker for the PlayCanvas Editor
https://playcanvas.com/
160 stars 28 forks source link

Hierarchy Search - is very slow. #1229

Open Maksims opened 3 days ago

Maksims commented 3 days ago

Previously searching through hierarchy was pretty fine, but I've noticed it was changed the way the results are visualised in DOM, which results in massive lag spikes (500-2000ms stalls) when scrolling or even selecting elements from the results list.

Here are the reasons:

  1. It uses tree items, assigning classes to them if it should be shown or hidden. This means that iteration through all DOM elements of the hierarchy is needed for each search result change. This is very slow, as it will lead to each DOM element recalculation.
  2. Each element uses display:flex - this is also an issue, as use of flex is way less performant in browsers than display:block. This affects many areas, even scrolling, as it has to recalculate size of an element based on flex rules and elements interacting between each other. So when scrolling - this leads to massive lag spikes. The number of elements within flex element (even hidden) is a culprit. Consider avoiding use of display:flex as much as possible in context of large elements quantities.
  3. Elements do not use contain, which can help a to instruct a browser of how element can change its size and affects rendering, which can speed up relayout and rendering by browsers significantly.
  4. Due to use of flex and complex styling rules, when clicking on an element, it leads to a massive stall as it has to perform Hit Test within DOM, which forces relayouts. display:flex - makes it worse. Compare how quick selection works in normal hierarchy versus hierarchy search results.

What can be done:

  1. Do not modify hierarchy tree DOM, use own element with only elements added to DOM that are the results of a search.
  2. Do not use display: flex on parent element with large number of child elements.
  3. Consider adding chunked elements creation, where only 128 of elements created first, and if scrolled enough, another 128 would be created until results are empty. Developers usually need either early results, or sometimes want to select all items within results. With known height of each element, it is possible to ensure inner element is the right height for scrolling to appear as needed.
  4. Ensure that element that is added is very simple, no many classes, just icon and name of entity.
  5. Use content:strict on each element as well as its container with pre-set height.
willeastcott commented 3 days ago

I've moved this issue to PCUI since it's not specific to the Editor.

willeastcott commented 3 days ago

I just hacked the TreeView example to generate 11,111 TreeViewItems:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>PCUI TreeView</title>
    <meta charset="utf-8">
    <style>
        body {
            background-color: #364346;
        }

        .font-regular, .font-bold, .font-thin, .font-light {
            font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;
        }

        .pcui-treeview-item-text, 
        .pcui-treeview-item-icon {
            font-size: 14px;
        }
    </style>
    <script type="importmap">
        {
            "imports": {
                "@playcanvas/observer": "https://cdn.jsdelivr.net/npm/@playcanvas/observer/+esm",
                "@playcanvas/pcui": "http://localhost:3000/dist/module/src/index.mjs",
                "@playcanvas/pcui/styles": "http://localhost:3000/styles/dist/index.mjs"
            }
        }
    </script>
</head>
<body>
    <script type="module">
        import { TreeView, TreeViewItem, TextInput, Container } from '@playcanvas/pcui';
        import '@playcanvas/pcui/styles'

        const container = new Container({
            flex: true,
            flexDirection: 'column',
            width: '100%'
        });

        const filter = new TextInput({
            keyChange: true,
            placeholder: 'Filter',
            width: '200px'
        });
        filter.on('change', (value) => {
            treeView.filter = value;
        });

        const treeView = new TreeView({
            allowRenaming: true
        });

        const root = new TreeViewItem({
            text: 'Root'
        });
        treeView.append(root);

        const generateChildren = (parent, depth) => {
            if (depth > 0) {
                for (let i = 0; i < 10; i++) {
                    const item = new TreeViewItem({
                        text: 'Item ' + i
                    });
                    parent.append(item);

                    generateChildren(item, depth - 1);
                }
            }
        };

        generateChildren(root, 4);

        container.append(filter);
        container.append(treeView);
        document.body.appendChild(container.dom);
    </script>
</body>
</html>

I really don't see any meaningful lags. Can we isolate what you're seeing in a simple example like this?

Maksims commented 2 days ago

Hi @willeastcott, not sure if it is PCUI related, it could be the way Editor uses that TreeView and the complexity of other things. Please open any large project, perform hierarchy search and within results scroll & select entities, it will freeze during selection and during scrolling.

image

Here is a video with lags of 2-3-5 seconds on selection, with freezes after selection, notice how hovering does not appear on tree items and it constantly freezes whole tab.

https://github.com/user-attachments/assets/34f1ef06-4b5f-4cc4-9fac-d375a1da0bf7

willeastcott commented 2 days ago

LOL, OK, I've moved the issue back to the editor repo. 😄