Search Unity

Question How to make a performant GridView

Discussion in 'UI Toolkit' started by MechaWolf99, May 4, 2021.

  1. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    Hello, I am trying to make my own GridView element that mirrors the features of something like the Project Browser grid. However I am running in to performance problems when trying to resize the item elements in realtime (like with the a slider to change their size).

    The way I have it setup right now is that all of the item elements have their position set to absolute, and I am just manually setting their position. (each item's flexGrow and flexShrink is set to 0).
    To note, the container they are in is just a VisualElement with flexGrow set to 1. Nothing else has been changed on it.

    When resizing, I am just looping through all of the item elements and setting their width, height, and position. It is okay until you get around the 350-400 elements mark and then the performance goes down pretty fast. I understand that is a lot of elements, but I was hoping to match (or get close to) the performance of IMGUI.

    At this point I am kind of assuming that this is just a limitation of UIToolkit, but I thought I would ask here if there were any other things I could do to increase the performance.

    I should note that this test doesn't handle hiding/showing items when scaling, it only moves and resizes the elements. I also haven't even touched figuring out how to handle scrolling yet.
     
  2. SimonDufour

    SimonDufour

    Unity Technologies

    Joined:
    Jun 30, 2020
    Posts:
    575
    Hi!

    There is currently no gridview component.

    But there is a listView that could help you with performance as it does not display what is not shown.

    This user recently went in that direction to display thousands of images: https://forum.unity.com/threads/hit...ews-custom-asset-loader.1095310/#post-7103815


    You should still be able to modify more than 350 elements: can you do a capture with the profiler and share it so that we can see what takes the performance down? There may be something that could be changed quickly.
     
  3. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    Thanks for getting back to me. Yes, I am aware there is no GridView element, that is why I am making my own.
    I actually originally used the ListView, but ended up have pretty bad performance when resizing as it would have to remake all of the items again.

    I don't feel like I explained what the problem is well, sorry. I want to have ~400 elements on the screen at once, and change their size/position. So having it virtualized wouldn't help.


    Here is the profiler while resizing the item elements(I was constantly resizing it). I can save it and send the binary if that would help.


    And just incase you want to see for your self. Here is the element and a test window. Link.
    The script is just to test the performance when updating a large number of items at once. I understand that it could be virtualized, so that when the items are bigger the ones on screen are not updated or rendered.

    I hope this helps explain it a bit better. Let me know if you have any ideas.
     
  4. SimonDufour

    SimonDufour

    Unity Technologies

    Joined:
    Jun 30, 2020
    Posts:
    575
    Thanks for sharing more details on your specific usage.

    By changing the width, we have to regenerate all the geometry of the elements, and it is what takes the most of the processing time. I know it is probably part of your design, but if you remove the rounded corners and the borders the tessellation should be quicker. I would be curious to test out if a 9 slice is also quicker to tessellate.

    If the size of your borders are not important, you could also change the transform of the object and that will be much more efficient. This will stretch them so visually, so the tessellation step won't exist at all. One of the downside is that a border will vary in thickness: one that was 1px will reach 2px when scaled by a factor of 2, and it is going to be especially noticeable if you don't have uniform scaling (different scaling in x and y) as the border on the sides will have a different thickness compared to the one on top/bottom. That being said, because you are doing square element, it may not be noticeable.

    If you go in that direction, I would recommend you to do the following: Instead of stretching every element individually, you can stretch their parent and use a group transform usage hint. This will let the GPU adjust all the mesh instead of the CPU. The simplest way to achieve this is to use the ScrollView as there is already the hint to help with the scrolling and resize the content container.

    To change the scale, you will currently have to use the visualElement.transform interface in C#.

    You can see one example of such resizing in shader graph and vfx graph
     
  5. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    Ah, good to know more about how UIToolkit works. The rounded borders are not needed, it was just quick to use buttons for displaying.

    In the end, what I am trying to do is basically recreate the grid from the Project Browser.
    Are you able to explain when and how the usageHints are used. It is not clear from the docs what elements they should be used on, and what cases the increase performance for.

    Can you say what part of the shader graph? I have looked through a lot of it before to learn how the GraphView works. But that would be like trying to find a needle in a haystack.
     
  6. AlexandreT-unity

    AlexandreT-unity

    Unity Technologies

    Joined:
    Feb 1, 2018
    Posts:
    377
    Assuming that the cell content doesn't move, the "container" of your grid should use a DynamicTransform hint, and you should set its transform to move/scale the grid. By doing so, when the grid is moved around, the transform will be applied on GPU.
     
  7. SimonDufour

    SimonDufour

    Unity Technologies

    Joined:
    Jun 30, 2020
    Posts:
    575
    These usage hints are made so that they do not affect the visual, but they do offer performance improvements in some conditions. The best way to see if there are relevant is to actually benchmark your usage.

    https://docs.unity3d.com/ScriptReference/UIElements.VisualElement-usageHints.html
    https://docs.unity3d.com/ScriptReference/UIElements.UsageHints.html

    The usage hint is used when moving and resizing the "viewport" using the mouse wheel. We are not regenerating the whole content to change where/how big it is displayed. I think we may be regenerating the text, but not the borders, backgound etc...
     
  8. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    I tried both, and neither seemed to have any effect.

    Is there maybe some way to batch the generate of the meshes or something since they are all exactly the same? Or maybe some how just generate one mesh and copy it to all of the others elements?
    The scaling option works, but is a rather ugly way to do it I feel, and while I don't need borders right now it would at least be nice to have the option for them.
    I could also move to using IMGUI for it, but I would really rather use UIT.
     
  9. AlexandreT-unity

    AlexandreT-unity

    Unity Technologies

    Joined:
    Feb 1, 2018
    Posts:
    377
    We don't have this kind of optimization yet, but it's been discussed internally for use cases like GraphView derivatives (e.g. ShaderGraph) that have many identical nodes. However you could achieve this optimization on your end, through a generateVisualContent callback. For instance, the grid itself could be a single VisualElement and you could generate a custom geometry for it.
     
  10. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    I think it would be very nice if you had something like this. Eventually you will probably have to come up with a solution to this problem as well if the plan is to move all of the editor to UIT.

    generateVisualContent seems like an interesting solution. Is there somewhere I could look to see how to use it in action?
     
  11. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    After looking in to generateVisualContent, it seems a bit too low level for this case since I also want to have text and asset icon textures. Maybe I am mistaken though?
     
  12. AlexandreT-unity

    AlexandreT-unity

    Unity Technologies

    Joined:
    Feb 1, 2018
    Posts:
    377
    I was thinking you could use it for the background grid only. Your visual tree could look like this:
    • GridControl
      • View <= UsageHints.DynamicTransform (the one that gets scaled/translated)
        • Cell1
        • Cell2
        • CellX
        • Grid <= generateVisualContent
    With this scheme, the cells would be borderless and the grid would be generated once and drawn over the cells. The cells and the grid could use absolute positioning.
     
  13. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    I don't quite get what you mean about being drawn over the cells. But either way, if I need to have cell elements still, I would still need to set their width/height or scale them. And I have a dynamic number of columns and rows, the number of each is just however many items can fit. I want it to act like the ProjectBrowser grid view.
     
  14. AlexandreT-unity

    AlexandreT-unity

    Unity Technologies

    Joined:
    Feb 1, 2018
    Posts:
    377
    I meant if you want to display an actual grid (like a border around every cell). It could have been drawn by a single element. In the case of the Project Window, the scaling is fixed and there are no borders around the cells though.
     
  15. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    Oh got it, that makes sense. What? The scaling is dynamic in the project window. There is the little slider at the bottom right that changes it.
    I think right now the answer is just to use IMGUI for it. The performance just isn't there for this case, at least when compared to the performance of the grid in the project browser.
    If some sort of batching or copying is added then I think this would fix the performance problem and a very nice scrollable GridView could be made with UIT. Would there be a good place to make a feature request for both the batching stuff and for a proper built-in grid system?
     
  16. AlexandreT-unity

    AlexandreT-unity

    Unity Technologies

    Joined:
    Feb 1, 2018
    Posts:
    377
    If you want to get good performance, avoid changing the size of cells through the style, use the their transform instead, and if you plan to have them move around independently of the scrolling, set the DynamicTransform hint on them. Then the "panner" or "view" should have GroupTransform instead, since it contains tons of items with DynamicTransform. When scrolling, only change the view transform position. When changing the size, just scale the cells and move them as you wish to "wrap". This is how ShaderGraph is implemented (except that nodes don't wrap, but it's equivalent to moving nodes around).
     
  17. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    If I use the scale instead of using the style, then any text would also become much larger/smaller when scaling which is not desired.
    So should all of the cells have the DynamicTransform hint? From the docs it sounded like it should be used sparingly.
     
  18. AlexandreT-unity

    AlexandreT-unity

    Unity Technologies

    Joined:
    Feb 1, 2018
    Posts:
    377
    We don't want them to be used "just in case", without actual reason, because then, they can degrade performance. It has to be a tradeoff. In your case, if scaling the cell content is a problem, don't hesitate to use it.

    In your case, it seems that the text of the cell footer may have to be regenerated, but the content itself could be preserved and scaled. What is the content? Is it just a quad?
     
  19. MechaWolf99

    MechaWolf99

    Joined:
    Aug 22, 2017
    Posts:
    294
    Ah, that makes sense. It would be nice if the docs were a bit more clear on what it is that they actually optimize and some examples of when they should/shouldn't be used.

    In the end what I want the content to be is a main thumbnail Image element, a smaller image element in the bottom right corner of the main image element, and a label element below the main thumbnail element.
     
  20. AlexandreT-unity

    AlexandreT-unity

    Unity Technologies

    Joined:
    Feb 1, 2018
    Posts:
    377
    We're planning to create some samples that will illustrate this better.

    In this context, I think you could get good performance if you used generateVisualContents to draw all your quads at once (maybe split them vertically into 4-5 large rows). I'm a bit worried about your text elements though. I suppose that you will be regenerating the text over and over as you scale, as the available horizontal space will increase/decrease per size, modifying the generated text mesh. Scrolling should be fast if you set the proper usage hint on your "view".

    Unfortunately the geometry generation is not in a great state right now, performance-wise, so avoiding it whenever possible is the best solution. That's what we did in Graphview with UsageHints to avoid regenerating as we pan/zoom and move nodes around. But be reassured that it's on our radar and it will eventually be improved. We are explorating different strategies to address the issue, like the job system, mesh caching/reuse, compute, burst, etc.