How do I work out what to redraw in onUpdate()?

Hi. I'm sure this question must have been raised before, but I can't find an answer.

In the pre-OLED days, I think I understood how onUpdate()/onPartialUpdate() were supposed to work. With the Venu 2 (and, I guess, other OLED devices) things seem to be different. When the watch is woken by a button-press or gesture, onUpdate() gets called repeatedly -- perhaps ten(?) times -- at one-second intervals. Then the watch goes back to sleep, and it doesn't get called again until the next minute.

Meanwhile, with the OLED watches, it looks as if onPartialUpdate() is never called at all.

So -- suppose I want to update a small area of the screen when the watch is awake -- perhaps a seconds display. I don't see any alternative to drawing the entire screen every time onUpdate() is called. There doesn't seem to be any way to work out what actually needs to be updated. This seems like a waste of battery, erasing and redrawing the entire screen every second, when only a tiny region has changed.

And what happened to onPartialUpdate()? It is still called on the Vivoactive 3 (maybe others as well), but is never called on the Venu 2.

So, in short -- with the OLED watches, how do I update a small screen area only at 1Hz?

Have I missed something?

  • onPartialUpdate is only called on watches with a MIP display (and not older ones)

    For onUpdate() you always want to display everying that's on the screen.  On some devices, the dc is cleared before onUpdate is called - you won't see this in the sim).  Plus, on a real device, things like toasts/notifcations may have overlaid part of the screen and in an app you don't know when this happens or how much of the screen is involved.

    Whwn a watch face is in high power mode, onUpdate is called every second, for say 10-20 seconds, then onUpdate gets called every minute.  You can tell what mode you are in with onEnterSleep (once a minute updates) and onExitSleep (every second updates)  In the sim it doesn't automatically change.  You set the mode with..Settings>Low Power Mode

  • Thanks for responding. I understand how it works -- I just don't understand why it works as it does, and how to avoid it being a terrible battery drain.

    If I have to redraw the entire screen on every call to onUpdate() -- and it seems I do -- how to I prevent that being a battery drain, when all that's changed since the last update is a few pixels?

    I can't even try to keep track of whether something has actually changed (e.g., waking from sleep), and only update the screen when there is definitely a need to because, as you say, the screen has usually already been cleared before onUpdate() gets called.

    This just seems like an odd design strategy to me, unless I'm just missing something. Am I? I suppose I could just cache the screen contents in a buffer, and just dump the buffer to the screen in onUpdate() -- at least that would avoid the recomputation. Do other developers have a clever way to deal with this, or does everybody just quietly ignore the problem?

  • The battery drain on AMOLED isn't the drawing, but the number of pixels that are on.

    You can also save battery with when you calculate things vs displaying things.

    Take for example "sunset".  There is no need to calculate that each time onUpdate is called - only when the day has changed. so most of the time, you just display what was calculated before and don't calculate it..

    When onUpdate is called the FW updates the whole screen.  so if it's one character or 1000 that changed, the cost is the same.

    With onPartialUpdate/setClip you can limit how many rows get updated.  If you don't set a clip region, you'll easily exceed the power budget in low power mode.  The only time it's safe to only update part of the screen is with onPartialUpdate. 

    Again, don't trust how the sim works for different devices, as it also doesn't have toasts and notifications.

  • Hi. You say "The battery drain on AMOLED isn't the drawing, but the number of pixels that are on" But are you really sure about that? Does it not depend on what actually has to be drawn?

    I'm displaying (for example) a simple analogue watch face with no second hand. There's no need to redraw any part of this display other than once per minute. When the watch wakes up, it actually calls onUpdate() ten times at intervals of a second or less. One of these updates draws the relevant content, and the next nine draw it again, unchanged. I can't prevent this happening, or take any shortcut, because the watch actually erases the screen before each call to onUpdate() (I checked).

    The number of pixels illuminated is not a relevant consideration here: exactly the same number of pixels will be lit on every call to onUpdate(), because each call will draw exactly the same content. But the screen gets redrawn with identical content nine times.

    Is it really the case that keeping the OLED display on for ten seconds will use significantly more battery than the nine worthless updates? How would we even confirm or refute such a thing? I would expect that font rendering -- if you're displaying text -- alone would be quite compute-intensive.

    You say "With onPartialUpdate/setClip you can limit how many rows get updated." But, in fact, onPartialUpdate() is not called on the Venu2 (I checked). I can't rely on it to do anything.

    I'm trying to design a watch face that uses the absolute bare minimum of energy. I've already cut the number of lit pixels to the smallest that is readable. Now I want to cut all the useless screen redraws. But it seems to be impossible to do so.

  • This topic (only updating part of the screen in onUpdate).  He's a post py one of the Garim folks:
    https://forums.garmin.com/developer/connect-iq/i/bug-reports/vivoactive-4-clears-screen-before-onupdate?CommentId=414d42e0-b70a-43e3-bff5-422895f94dae

     and another post by Travis in another bug report: https://forums.garmin.com/developer/connect-iq/i/bug-reports/screen-refresh?CommentId=023bd8e8-5754-4ab3-b5d0-3612726e779f

    What you want to do has been attempted by many, and the only solution is to update the entire screen when onUpdate is called.

  • OK. Thanks for following up. So it seems that the way that onUpdate() currently works makes it difficult to implement a watch face that uses no extraneous battery at all. The Garmin folks already know this, and aren't going to change anything.

    That's hugely disappointing, but at least I won't have to waste any more time trying to implement something that is impossible. Thanks.

  • The battery drain on AMOLED isn't the drawing, but the number of pixels that are on.
    You say "The battery drain on AMOLED isn't the drawing, but the number of pixels that are on" But are you really sure about that? Does it not depend on what actually has to be drawn?

    I think he means that for the AMOLED *display* itself, the battery consumption is based only on the number of pixels that are lit for a given period of time, and doesn't depend on whether the set of lit pixels is changing or not. Whereas for MIP displays, IIRC, battery consumption is based on the number of pixels whose state changes over a given period. IIRC, the power cost to persist a static image on a MIP display is supposedly 0 (for the display itself).

    The first point would imply that displaying a static image on an AMOLED display takes the same amount of energy as displaying an animation (with the same number of pixels as the static image). It sounded wrong to me, but I'm not an expert. This suggests that it might be right:

    https://library.imaging.org/admin/apis/public/api/ist/website/downloadArticle/cic/26/1/art00005

    Since the power cost of an image in an OLED display depends mainly on the content and the RGB values of the individual pixels,

    Regardless of the above point, it stands to reason that executing the code for drawing would consume battery (due to CPU/GPU usage). And that's the point that wasn't addressed except to say you always have to draw the whole screen in onUpdate(). Unfortunately, this is the case, because even if the device didn't clear the screen before onUpdate(), as mentioned above, there's always the chance a notification will be drawn on top of your app, and there's no way to know which (additional) part of the screen has to be redrawn in that case.

    We could say it's a design that we don't agree with, but unfortunately I don't think there's any way around it.

  • When onUpdate ends, the entire dc gets displayed, so battery impact is the same.  On MIP devices with onPartialUpdate/clip regions, only part of the display gets updated.

    in a widget/glance/device app, the developer can control how often onUpdate is called, but not in a watch face or data field.

  • "We could say it's a design that we don't agree with, but unfortunately I don't think there's any way around it."

    The way around it is not to waste time developing for something that seems so broken, with respect to power management.

    If the user has decided, by choosing a particular watch face, that he or she is happy with an update every minute, it makes no sense to force the developer to write a pointless update every second, even for a relatively short period of time. It's just pointlessly wasteful.

    I don't know how much CPU-related energy these pointless updates consume, compared to the energy used to light the display. But my point is that, even if this energy is small, it isn't _zero_, and it _should be_.

    This problem could easily be solved in the ConnectIQ API, with two simple changes:

    1. The watch never erases the display before calling onUpdate() -- it's the watch face's job to do that, if it needs to

    2. The call to onUpdate() has a flag that indicates whether the display has been changed by the system since the last call to onUpdate(). If the display has not been changed since the last call, the WF app can work out whether any of _its_ information has changed, and decide whether to do anything at all in this particular cycle.

    Because it seems to me that this could so easily be done properly, it seems odd that it isn't. I guess there are historical reasons why the API works the way it does. I can't think of any logical reason.

  • Run a native WF for a few days and observe the battery usage, then run yours.  Yours will likely be slightly higher as native watch faces don't use CIQ, and can do many things a bit more efficiently. But the difference shouldn't be major.