Search Unity

Unity UI Lag in updating UI

Discussion in 'UGUI & TextMesh Pro' started by immrATaylor, Aug 19, 2019.

  1. immrATaylor

    immrATaylor

    Joined:
    Mar 28, 2017
    Posts:
    26
    I have code that changes the color of an image when a touch screen on Windows 10 is touched. When the screen is touched I also have an external function that I call to toggle a GPIO pin on a board. My coworker ran some tests with an optical sensor to see the latency on the color changing. We always have 60+ fps. The code for the color change is before the code toggling the GPIO pin but the color change is actually occurring 100 ms after the GPIO pin is toggled(My coworker is using a program that shows him when the screen is touched, when the pin is toggled, and when the color change occurs). I tried calling Canvas.ForceUpdateAllCanvases and I also tried LayoutRebuilder.ForceRebuildLayoutImmediate but this did not affect the time taken for the actual color change to occur. Is there any way to reduce this latency? Am I doing something wrong somehow?
     
  2. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    the only thing which pops into my mind: Maybe your coworker measures the time from when the surface is hit. And maybe you are using a "OnClick" function which is toggled when the surface is released. That could result in a time difference of 100 ms.

    If I am correct you should add an
    EventTrigger
    and use the
    PointerDown
    event instead of
    OnClick
    .
     
  3. immrATaylor

    immrATaylor

    Joined:
    Mar 28, 2017
    Posts:
    26
    @Hosnkobf I have code that changes the color in onpointerdown on a image and then I have another scene that does the following in Update:
    Code (CSharp):
    1. private void Update()
    2.     {
    3.         if (Input.GetKeyDown(KeyCode.Escape))
    4.         {
    5.             Application.Quit();
    6.         }
    7.  
    8.         if (Input.touchCount > 0 && TestRegTouchObjs.activeSelf == true)
    9.         {
    10.             Touch touch = Input.GetTouch(0);
    11.             if(touch.phase  == TouchPhase.Began)
    12.             {
    13.                 if(InputTestImage.color == defaultColor)
    14.                 {
    15.                     InputTestImage.color = touchedColor;
    16.                 }
    17.                 else
    18.                 {
    19.                     InputTestImage.color = defaultColor;
    20.                 }
    21.                 LayoutRebuilder.ForceRebuildLayoutImmediate(InputTestImage.GetComponent<RectTransform>());
    22.                 WriteReadTestC();
    23.             }
    24.         }
    25.      
    26.     }
    The results are the same. And yes the program starts measuring time from when the surface was hit.
     
  4. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    I guess with "onpointerdown" you mean that you implement
    UnityEngine.EventSystems.IPointerDownHandler
    . That is how you should work with UI objects. You never work with colliders and "magic functions" there.

    note that
    LayoutRebuilder.ForceRebuildLayoutImmediate
    just rebuilds the layout without showing it immediately. There is no way to force drawing on the screen immediately. I guess you are doing something afterwards which takes some time (probably in
    WriteReadTestC
    ). So, you either need to wait a frame (via coroutine) before doing expensive stuff or doing the expensive stuff in another thread (which is not so easy with unity).

    here is an example for the coroutine approach:
    Code (CSharp):
    1.         private void Update()
    2.         {
    3.             if (Input.GetKeyDown(KeyCode.Escape))
    4.             {
    5.                 Application.Quit();
    6.             }
    7.    
    8.             if (Input.touchCount > 0 && TestRegTouchObjs.activeSelf == true)
    9.             {
    10.                 Touch touch = Input.GetTouch(0);
    11.                 if(touch.phase  == TouchPhase.Began)
    12.                 {
    13.                     if(InputTestImage.color == defaultColor)
    14.                     {
    15.                         InputTestImage.color = touchedColor;
    16.                     }
    17.                     else
    18.                     {
    19.                         InputTestImage.color = defaultColor;
    20.                     }
    21.  
    22.                     // we do not need to rebuild the layout by hand
    23.  
    24.                     this.StartCoroutine(WriteReadTestCDelayedRoutine());
    25.                 }
    26.             }
    27.        
    28.         }
    29.  
    30.     System.Collections.IEnumerator WriteReadTestCDelayedRoutine()
    31.     {
    32.         yield return new WaitForEndOfFrame(); // you can also yield return null
    33.  
    34.         WriteReadTestC();
    35.     }
     
  5. immrATaylor

    immrATaylor

    Joined:
    Mar 28, 2017
    Posts:
    26
    @Hosnkobf
    All that the WriteReadTestC function does is toggle a GPIO pin on a board as far as I know(It calls a C++ dll function that I didn't write). I tried removing that function but the results are the same. It doesn't make sense that it is taking 100 ms to update. And when I say I am using OnPointerDown I mean I have attached an event trigger component to a image and added the PointerDown event to it. The documentation seems to suggest that the UI should be updating at the end of every frame which means it should be taking 16,17 ms, I believe, and not 100+ ms.
     
  6. AlexHell

    AlexHell

    Joined:
    Oct 2, 2014
    Posts:
    167
    You may have only 2 variants:
    1) your "color update" code is slow.. to test it - you should measure it, foe example with Stopwatch \ or DateTime.UtcNow
    2) if (1) is false: your image update is delayed
    Can you provide the deep inside your rendering? What are the "color update" technique? Did you change Color on Material? (which change shader _Color property)? Did you change the Texture2D pixels?
     
  7. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    3) some other code somewhere in your project is responsible for the delay.

    You should attach the profiler to get an idea.
     
  8. immrATaylor

    immrATaylor

    Joined:
    Mar 28, 2017
    Posts:
    26
    @AlexHell What do you mean by depe inside your rendering?
    This is my hierarchy. I have highlighted the image that my code I have posted above is changing. I'm changing the color of the image component. I don't think that would change any Texture2D pixels
    upload_2019-8-22_12-34-40.png

    @Hosnkobf
    I have tried looking at the profiler but honestly, I don't know how to read it really. Nothing on there jumps out to me as a problem. I tried having it show rendering and UI only.
     
  9. AlexHell

    AlexHell

    Joined:
    Oct 2, 2014
    Posts:
    167
    try use
    Code (CSharp):
    1.  
    2. Debug.Log("start")
    3. if(InputTestImage.color == defaultColor)
    4.                     {
    5.                         InputTestImage.color = touchedColor;
    6.                     }
    7.                     else
    8.                     {
    9.                         InputTestImage.color = defaultColor;
    10.                     }
    11.  
    may be with DateTime.UtcNow or DateTime formatted
    probably your code is not invoked
    as I see - color must be applied immediately, it change shader' _Color property, when default shader
     
  10. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    Maybe you can find the slow part of your code with a Stopwatch.
     
  11. immrATaylor

    immrATaylor

    Joined:
    Mar 28, 2017
    Posts:
    26
    @AlexHell @Hosnkobf This is a screenshot of what StopWatch.GetTimeStamp is returning. I am calling it right after I change the color. The first time I called the function it returned 2496189897920
    upload_2019-8-26_12-3-14.png
     
  12. immrATaylor

    immrATaylor

    Joined:
    Mar 28, 2017
    Posts:
    26
    The code itself seems to run fine, the problem is when the UI actually updates.
     
  13. Hosnkobf

    Hosnkobf

    Joined:
    Aug 23, 2016
    Posts:
    1,096
    set the transitions on the "Test Latency" button to none and add another canvas to the image which changes the color. This should improve the performance of the UI a lot because then only the image would be updated and nothing else.
     
  14. immrATaylor

    immrATaylor

    Joined:
    Mar 28, 2017
    Posts:
    26
    @Hosnkobf I tried your suggestion but nothing changed. The latency remains the same. Here is a video that my coworker made which shows the latency issue:

    I have also submitted a bug report to Unity.
     
  15. AlexHell

    AlexHell

    Joined:
    Oct 2, 2014
    Posts:
    167
    Sorry, but your video is show nothing to me, as it does measure your dedicated software, not unity, and all of this "signals" is not related to unity, and all of hz is not related to unity, etc

    you need to create more reproducible test case, where not use your software, for example create stub with pregenerated signal of sin\ meander\ etc, and try to update UI.. your logic is very coupled now, but it must be decoupled: UI must be not related to signals or other software