How to determine if a watchface is inside a view transition

The docs (https://developer.garmin.com/connect-iq/api-docs/Toybox/WatchUi/View.html#onUpdate-instance_method) say, that onUpdate may be called multiple times during view transition.

My watchface does make partial updates to increase it's performance but does make full updates at the very first layout and when it comes back from sleep. I would need to also find out if my view is inside a transition currently because I will need to make a full update in the first onUpdate after this transition, otherwise the full update will only be made current the transition and this leads to invalid displayed view...

Problem

Currently, if I start an app and then go back to my watchface, there is an animation with a semi transparent white overlay - in my partial updates I do not draw all parts of my watchface but rather one part after another and this means I can see this semi transparent white overlay from the transition until the corresponding area is redrawn

  • There is no functionality available to tell if you are in a view transition or not. You should redraw the entire screen when the system calls View.onUpdate(). If the system calls WatchFace.onPartialUpdate() you can just update the stuff that is changing.

  • You should redraw the entire screen when the system calls View.onUpdate()

    Why shouldn't I optimise here? Most of the screen of a watchface does not change every second (most data even does not change within an hour).

    Here's a short background why I started making partial updates:

    • my watchface shows up to 14 different datas
    • it shows up to 8 data fields with rotated fonts in a circular way
    • fonts for the circular data must be loaded and unloaded each time they are used (one font per letter, too many fonts to keep in memory)
    • a single onUpdate may need 100ms, sometimes even more, depends on user settings
    • before you can start an app you need to wait 4s on a Fenix 6X

    After starting refactoring my code and doing some partial updates I could improve my watchfaces performance already quite noticeable - I can start an app by only waiting 1.5s to 2s (onUpdate went down to 40ms). Shorter onUpdate durations will lead to less battery usage and a more responsive watch.

    What would you suggest instead? Is there any other drawback with partial updates other than the one I face with transitions?

  • When a watchface first starts up, it's in high power mode, and onUpdate() gets called a number of times (maybe 10-15).  In high power mode, onPartialUpdate isn't called.  It's like after a gesture, the WF switches to high power mode for around 10 seconds (no calls to onPartialUpdate) then drops back to low power mode.

    As Travis said, you want to draw the entire screen in onUpdate()

  • One thing to consider as far as speeding things up is not what you display, but how you calculate what to display.

    An example here would be sunrise/sunset.  No need to calculate that every time onUpdate() is called - only when the day changes.

  • I tested my code and calculation of ALL data (even if I repeat it each time) is neglectable (1-2ms). The big issue in my case is drawing - loading/unloading fonts is a heavy task and I have to load 20 to 30 fonts to draw my circular data...

    Again, what does speak against making partial updates in onUpdate? Is it just that the framework does not provide me the information about the transitions? Or is there anything else?

    I tried it in the sim of all F5/F6 and FR devices and it seems to work flawless (only the transition on a real device makes problems, but I could "solve" that with a rather hacky way).

    EDIT:

    I would not mind using an offscreen buffer instead and making my partial updates on that but based on the big amount of data and settings my watchface has there is no memory left for a full offscreen image. So this is no alternative for me...

  • You're calling onPartialUpdate from onUpdate()?  Probably not a good idea, as you'll be doing stuff for full screen you don't need to do, like setting a clip region.

    What you can do is something like this:

    function onUpdate(dc) {

      displayStuff(dc,true):

    }

    function onPartialUpdate(dc) {

      displayStuff(dc,false);

    }

    function displayStuff(dc,isFull) {

      if(!isFull) {

        //set clip regions, etc

    }

    //display what you want to display

    }

    It sounds like what you're running into is loading all the fonts.  Can't you just load the ones you need at that time and not all 20-30?

  • No, I'm doing exactly what you suggest. I call functions from onUpdate and onPartialUpdate.

    My normal partial updates do not use clipping (would not help me, I need to clip a circular range) - I do overdraw the desired region with my background color instead to "clear" it (with drawArc and a fitting pen with).

    I'm loading font by font and unloading it again - otherwise I would run out of memory. In each drawing round I only need each font once... So I'm only loading the ones I need when I need them and unload them again afterwards and load the next one...

  • If you're not using clipping, there's a very strong chance you will exceed the power budget and then onPartialUpdate will stop being called after a minute or two.

    You can add a delegate to your watchface to see if that occurs.  You can also put a println() in onPartialUpdate to see when it's called in the sim - just make sure in the sim you have it set for low power (toggle the setting a couple times - a known bug)  The average for the power budget is calculated over a full minute, so that's why it might not stop for a minute or two.

  • I'm sorry if I was unclear.

    • in onPartialUpdate I do use clipping - no problems there (no problems with the power budget, tested this already)
    • in onUpdate I do make partial updates but without clipping - no problems I'm aware of there but the transition

    I do reuse some functions (only one actually for seconds) in onPartialUpdate but this functions distinguishes between being called from onPartialUpdate and onUpdate.

    Everyrthing is working fine I'm still interested why you all say "do a full redraw in onUpdate" - why if partial updates do work, are faster and are of course more beautiful from a programmer's view - I did not find any pont that speaks against this technique (but transitions of course). I don't see any alternative way in my case to improve performance of my watchface

  • Why shouldn't I optimise here? Most of the screen of a watchface does not change every second (most data even does not change within an hour).

    I *believe* the big reason is that you don't know when part of your screen is obscured by a notification. If the system generates a notification that covers part of the screen, we call onUpdate() expecting you to redraw the entire screen when that notification is dismissed. If you don't update the entire screen then there will very likely be display issues in the area that was previously obscured by that notification.

    Of course the system could get around that by caching the portion of the screen that is about to be obscured and then overlaying it later, or we could add a new onUpdateView(rect) that told you where to update, but this is not how the system works.

    For a watch face to work properly on devices now, it has to work within the current system, and the current system expects you to update the entire view when onUpdate() is called. Given that we should only call onUpdate() once a minute, maybe a few times during a transition, and maybe a few times when an overlaid notification is displayed, this shouldn't be a huge power suck.