Re-render specific parts of the display in the onUpdate method

Hello!

I have written my own watch face and I was trying to maximize battery life as much as possible. One consideration I had on mind was to only re-render the parts of the display I need to update, but I am struggling with that part.

Inspired by the onPartialUpdate() method, my idea was to re-render a small part of the display once a minute (the minutes value) and a full display update every hour (when minutes equals 0).

Something like this:

function onUpdate(dc) {
    if (needAFullRedraw) {
        ...
        View.onUpdate(dc);
    } else {
        dc.setClip(<dimensions>)
		dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
		dc.clearClip();

		dc.drawText(
				x,
				y,
				Gfx.FONT_SYSTEM_NUMBER_THAI_HOT,
				minutes.format("%02d"),
				Gfx.TEXT_JUSTIFY_RIGHT
			);

    }
}

When a full redraw is not needed (needAFullRedraw = false) the minutes show up just fine and are updated accordingly. However, the rest of the display turns blank and I do not see any of the existing data that was previously set by View.onUpdate(). At least on a real device, on the emulator is all good regardless of the high-power mode or low-power mode setting.

On my watch, I measured the performance by logging the elapsed time within the onUpdate() method and it is definitely faster when only the minutes are re-rendered. However, I do not understand why everything else turns blank. Am I not supposed to mix View.onUpdate() and partial updates in the onUpdate() method? Thank you in advance!

PS: I understand that the main use case of onPartialUpdate() is to show up the seconds for a short amount of time, I just want to extend the idea of re-rendering a small part of the display in the onUpdate() method.

Top Replies

All Replies

  • You always want to redraw the whole screen in onUpdate.  onPartialUpdate is the only place where you can just do a part.

    On some devices the screen gets cleared before onUpdate is called, on others, a toast can cover part of the screen, and these things vary by device, and you don't see either in the sim.

    Sounds like you are testing with a device that clears the screen, and you won't see that in the sim.

  • Oh, that explains it. Thank you !

    Yeah, my device is a Vivoactive 4S. In the emulator I would get redraw issues if I was not using the clip methods and therefore I assumed it was doable in the watch.

    On my device, a direct call to dc.drawText() was about 6 times faster than calling View.onUpdate(). Since I am doing a private face anyway (I do no even have settings) and want to optimize for performance and battery life, I wonder if it would be better to remove the layout and labels I have and do dc.drawText() calls directly. I know every case is different, but does it sound reasonable to you?

    Something I liked when using View.onUpdate() is that I do not need to update all labels in every onUpdate() call. For example, I would only call findDrawableById() and setText() for the hours label only when minutes value is 0. If I am going with dc.drawText() I won't be able to do anything like that. I do not know if it's going to be faster to have extra System and ActivityMonitor calls every minute or cache some values in a class variable since I do not need the real values every minute (I have read that accessing global variables is really slow, anyway that's easy to benchmark).

  • In general, doing direct dc calls is more performant in both memory and CPU usage, as the layout code has to handle anything, where dc calls can be more specific.  When you use layouts, what comes out the bottom end is pretty much dc calls anyway.  For CIQ 1 devices where DF's were limited to 16k, switching to direct dc calls was a common way to free up a bit of memory.

    Some things aren't going to change that often (like if activity tracking is on), but the data you get from ActivityMonitor, will change every minute (like steps)

  • Yeah, I imagine there's a lot going on behind the scenes in a single View.onUpdate() call. I will test removing the layout and labels and use direct dc calls instead. I believe View.onUpdate() needs to account for so many things that are not even applicable to my watch face.

    Surely steps will likely change every minute. In my case, I had a rounded value that was stored as a float and I would only call findDrawableById() and setText() if the new rounded value was different.

    Thank you once again!

  • Yeah, I imagine there's a lot going on behind the scenes in a single View.onUpdate() call. I will test removing the layout and labels and use direct dc calls instead. I believe View.onUpdate() needs to account for so many things that are not even applicable to my watch face.

    I did the changes yesterday and, in case anyone was curious or facing a similar situation, here are some benchmark data:

    - 33% reduction of memory usage.

    - Object allocation cut in half.

    - About the same amount of time spent within the onUpdate() method (or a marginal improvement of 1-2 ms at most). However, I also took away some compromises I had in place before (like the values of some watch complications were only updated every 5 minutes).

    As a bit of a closure, code is now a bit more verbose but overall I am pleased with the changes. Also, it's now easier to take extra compromises for the battery sake, for example not rendering any watch complication during night time.

  • Hi Jim,

    On some devices the screen gets cleared before onUpdate is called, on others, a toast can cover part of the screen, and these things vary by device, and you don't see either in the sim.

    Even in high power mode?

    I'm trying to not redraw anything after OnExitSleep, do I have to give up?

  • I would.  After a gesture onUpdate is called every second for 10 seconds.  And consider the venu and venu sq.  That's actually the only time you can do the full screen as when you are in low power, you have the burn in protections to worry about.

  • I would avoid relying on OnExitSleep() - it's not reliable at all. On devices with touch screens this method is never called when you tap on the screen and screen wakes up. I have a bug report out for this:

    forums.garmin.com/.../bug-report---wf-onexitsleep-never-gets-called-on-gesture

  • Hi, yes im aware about that, thanks.

    • The tap on touch screen and onExitSleep is linked with the backlight