Rendering WatchFace background screen

Hi!

I am new to programming Garmin watches so I started with a simple watch face. However, I am already stuck with the best way to create an energy efficient layout. I hoped to find a simple method how to set a static background and dynamic layers. Instead there seem to be dozens of ways how to implement this and the documentation does not provide a description which method should be used in which circumstance.

So far, I was lazy and did not want to deal with xml layouts. So I used the Dc object instead which is passed to onLayout(dc) and onUpdate(dc). I found that in this case, I can render any background by calling dc.draw... in onLayout(dc). Then I just create some layers by Toybox.WatchUi.Layer(...) which I update in onUpdate(dc). Now, I just read in the documentation "Note: You should never directly instantiate a Dc object, or attempt to render to the screen outside of an onUpdate call." - which I guess I am basically doing!?

I am kind of confused and annoyed by the different ways I could potentially solve the problem. Some things (like the layers) seem to be new in the SDK so that I could not find much in the forum.

Could anyone summarize the different methods which could be used for non-changing backgrounds and dynamic content, please? Which is the least energy consuming way to implement this?

Thanks!

  • I can render any background by calling dc.draw... in onLayout(dc)

    Do not draw anything in onLayout. You should do your drawing in onUpdate. Any drawing you do in onLayout should be moved to onUpdate.

    I'd be concerned with correctness as the first priority, then efficiency.. You're going to have to draw the full screen background on every call to onUpdate for correctness reasons. If you don't do this, you'll run into problems on some devices (venu and the vivoactive4 family of devices right now).

    If you're implementing partial update support, you can use a view clipping rect in onPartialUpdate to update just the stuff that you need to update for that second.

  • I am not sure that I understand 'correctness'. Do you mean that OLED displays would erase drawings drawn in onLayout() when you talk about 'correctness'? I could restrict the supported watches to MIP screen devices in this case. What's the issue with drawing in onLayout() otherwise?

    If I wanted to use clipping regions in onPartialupdate(), there would be screen total screen clearings every second when normal power mode is on. Is the power consumption of clearing the screen of MIP displays every second not significant in that case? Or do you assume that the normal power mode time per day is anyway relatively short for a regular user?

  • What's the issue with drawing in onLayout() otherwise?

    You already provided a reference to the relevant bits of documentation... Note: You should never directly instantiate a Dc object, or attempt to render to the screen outside of an onUpdate call. That should be enough to stop you from doing this.

    One example of why you shouldn't do it is that we have some devices that fully clear the screen before calling onUpdate and expect you to fully fill in the display. Whatever you draw in onLayout wouldl be overwritten before the user sees it.

    Not supporting OLED devices won't be sufficient to work around this. The vivoactive4, vivoactive4s, legacyherocaptainmarvel, legacyherofirstavenger, legacysagarey, legacysagadarthvader devices should all be affected and they use MIP displays. We are likely to add new devices that have this behavior. Also consider that since we explicitly tell you not to do this, it would not be inappropriate for us to make a change that would cause a change in behavior for existing devices. Just don't do it. Please.

    f I wanted to use clipping regions in onPartialupdate(), there would be screen total screen clearings every second when normal power mode is on.

    The screen is not cleared before a call to onPartialUpdate; this is guaranteed. If you want to clear the screen in onPartialUpdate, you can do that, but your watch face will quickly exceed the power budget and partial updates will be disabled until the watch face restarts.

  • One other instance is on some other watches, there is a small message displayed at the top of a screen (a "toast") for things like notifcations, sensor connects, etc), and if you don't do the full screen in onUpdate(), that part of the screen can be left in an odd state.

    Another thing to note is that onPartialUpdate isn't available on the venu, while it is on the newer MIP wearables (except the fr45/swim2)

  • Thank you for this clarification!

    Let me ask one last question about power consumption then: If an MIP display is not cleared, but the same pixels are overwritten by the same color, does that consume any energy?

    Instead of setting a background in onLayput(), I could do this by using a variable var firstUpdate which would turn true after the first drawing of the background in onUpdate() so that the background is not updated anymore. Would this also violate your idea of how to program a watch face?

  • If there is a seconds and running over some of these "toasts", then I can understand that there could be an odd state. However, if the display was splitted into some regions where there is no change at all, I would have imagined that one single update might be sufficient.

  • One of the things with MIP displays and onUpdate() in WF's is the majority of the time, onUpdate is only called once a minute.  After a gesture, it's called every second for about 10 seconds.

    So, onUpdate() and clearing the backround had minimal effect.

    onPartialUpdate is different (and not available on all devices, where it can update part of the display every second, as long as it stays under an average of 30ms per call.  If you are looking at saving battery, you'd save more by not using onPartiialUpdate or if you use it, keep the average as low as possible.

  • onLayout shouldn't be uses for drawing, and in the lifecycle of a WF is only called once.  It's where you define what to draw, and then onUpdate/onPartialUpdate does the actual drawing.