Real case of complex UI - Where can it be improved?

Hello, I’m here to show you my work and to seek for advices. I work in the home automation industry, we decided to use Unity as framework for the application that runs in the customer’s home touchscreen. This screenshot is an example of how the app will appear to the final user.

This seems a pretty simple scene but in low end devices (android chinese TV boxes as example) the app runs a bit cloggy (but to be fair, even in high end devices like a samsung galaxy 10 it is not so smooth)
you can check how the app runs here https://play.google.com/store/apps/details?id=it.hi.hivision.dovit&hl=it&gl=US

I have read alot about UI optimization and here is what i’ve done so far:

  • split canvas
  • avoid use of animator/animations
  • avoid use of layout components when possible
  • avoid use of mask in favour of the better mask2d
  • smaller optimization like pixel perfect disabled,
  • set static within inspector when possible
  • remove all the unnecessary raycast targets
  • use a simpler (and unique) version of shader optimized for mobile
  • batch images avoiding overlap

and, even if that some of those suggestions indeed overall improved the app, the user experience is far away from other UI applications made with other frameworks. Just to say, the “father” of this application was made in adobe flash and as3, a bit simpler but FAR FAR faster and smoother than this.

What I forgot here? or what can be done to improve / change this kind of application? Thanks in advance for any help or suggestion

Maybe my question was a bit too generic, I’ll add a specific case that i spotted but don’t know how to improve (or if is a real issue in the first place).

In the screenshot i posted here, if i use the frame debugger i can see 108 drawcalls and that number seems a lot to me.
I can see that the icons and the texts are batched and drawed in one single pass, but, there are a lot of overlapping that causes alot of calls beside the bathed ones.

Do you have any suggestion for improve this? Considering that the only elements that are immutable there are the top and left bar (that, still, took 12 calls to draw completely) but everything else is populated runtime driven by a data structure that is unique for each home system.

Moreover, are 108 drawcalls considered “too much” for a low end device?
I can see a clear “slowness” when the scene is drawing (I draw the single “tiles” in a coroutine, not alltogether) and the scrolling is “sticky” too but I don’t know if is the drawcalls the issue there.

Thanks in advance

Make sure Raycast Target is disabled on everything that doesn’t need to be one. E.g. a button should have a single transparent raycast target on the button itself, not all of its child images/text.

I don’t know exactly what affects draw calls, but what is your triangle count? Maybe that is an issue.

I would have each tile on its own canvas if there are animated parts inside it. If those animated parts have a lot of vertices, I would even put them on their own canvas. Are you animating them by scaling the canvas, or resizing their width/height?

Anything in a scrollview will be drawn again when it moves. If you have complicated things in there, maybe there’s a way to have canvases stacked in a scrollview so they won’t redraw, but you might have to position them manually using their height/width for spacing. I’ve never tried this.

The Rooms page runs smoothly for me, in both list and grid.

The sidebar menu only appears completely smooth when on the Plan page. Is it on its own Canvas? You can animate a canvas’s position without it redrawing.

Take a look at SpriteAtlas and these tips .

I’m sure you’ve seen this video, but here it is just in case.

There are indeed only one single raycast target in each of the “complex” object that should be touched. for instance: buttons, icons, tiles etc has a “fake mask area” (that consist on a UI-text empty, i saw it as suggestion somewhere here) as raycast target in front of all the graphical stuff. everything else has raycast disabled

6976691--822848--Schermata 2021-03-26 alle 10.18.42.png
Everything is just UI images and TextmeshproUGUI so the tris and verts count is always pretty low, i don’t know if we can blame that

About the animations: For all of them I use DOTween, most of them are just position transition or rotation, and a few scale animation, but not on entire canvases but for simple objects. Probably the side menu is the animation that involves more objects altoghether.

You mean by setting the canvas as World-space and animate its position? Actually all of the canvases are setted as Screenspace-overlay and all the animations are made on the rect-transform of the single object.
I could try to change the render mode but it would be a pain to adapt everything.

I have doubt about having a single canvas for each tile but I’ll try now. If i understand correctly the result will be, on the screenshot i sent, something like:

dashboard canvas
– scrollrect of tiles
– container
– canvas of tile “thermostat”
– canvas of tile “alarm”
… etc

that means that while scrolling the scroller will still have to redraw each canvas (instead of each tile), no? or, in paper, there should be an improvement?

I just use a transparent Image, but maybe a Text works fine too.

Yeah that seems like it’s not the problem.

I think just translating/scaling a canvas’s Transform will not redraw its contents. It does not need to be world space.

The hope is that it will not redraw each tile when scrolling, but it depends how the scrollrect code works. You can try just putting the scrollrect under a canvas to contain it from the rest of your dashboard and see if that makes a difference.

You need to use sprite atlas. 71 drawcall is a bit much. Always use sprite atlas

@jiraphatK I am indeed using sprite atlas. I have around 400 different icons in that app, divided in 2 atlases, and i can see from frame debug they are indeed batched and called once.

The problem i can see here is the overlap of elements, and I don’t know how to solve it. Example: That alarm tile take 18 drawcalls, i can see the text are drawn 4 times (1 on the white, 1 inside the button “apri”, 1 for bold, 1 for the timer) and basically 1 call each time an element is overlapping another, and seems sprite atlas wont help with that.

I thought to “pre-draw” some elements (like the button “on-off” with the text drawn instead of written with a text component) but even then there isn’t much space to play more than what is already done

Apart from what you have done

My project also used this simplified UI shader for the elements that don’t need masking.

I also use Procedural UI Image for common shape elements(round cube/circle) which results in much better edge anti-aliasing and you might have more space for other icons in your atlas. IDK about performance though but I just download your app and noticed a lot of jagged edge.

Also tried this empty graphic instead of empty text component

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
public class EmptyGraphic : Graphic
{
        protected override void OnPopulateMesh (VertexHelper vh)
        {
                vh.Clear ();
        }
}

Other than that, I don’t know what to suggest anymore.:face_with_spiral_eyes:

I use exactly the same UI shader with good results, and good call on that empty graphic script, I doubt it would be better in terms of performances, but it’s surely better compared to that “workaround” of text component.

About the plugin you suggested, I saw some times ago but I was scared that could be high demanding on performances. the idea of a better graphic is good, so I tried this morning and that is the result:

  • nice and clean edges
  • “harder” shadows, but not horrible
  • a very good drop on drawcalls. I managed to go from the 71 of that scene to 38 but…
  • a huuuge performance hit.

I now investigate more and I’ll write the result on the creator page too (maybe is helpful) but, tested the same scene on an android TV box (H96 Max) and it is barely usable, there’s like 1 frame per second. The only difference between before and after are that, instead of tiny png sprites with round edges, there are those procedural UI images

Thanks btw for the help, I start to think that it isn’t improveable more than this but is simple the unity limit

Those are the profiler of the application running on idle in the low end android device said before. A HUGE difference between the two. The first is totally unusable, the second (the original one of the first post here) runs, but really bad as said