Complete
over 2 years ago

BUG: some newer devices clear the screen on every view update. Simulator behaves differently

Some users have reported issues with my app Maze (https://apps.garmin.com/en-US/apps/3a4436a8-1126-4539-a2f2-aefb319a0ef0) and Snake (https://apps.garmin.com/en-US/apps/e443b0cc-8046-4d76-830f-e87517cf14b6), where the screen will go black after starting to play the game.

For efficiency, the screen is drawn using Dc calls, the view's onUpdate does not call the parent, and only the changed parts of the screen are redrawn: during gameplay on Maze, the previous location of the ball is blanked out, and the new location is drawn.  The event loop is run by a timer that calls Ui.requestUpdate() every 100ms.  This method has worked since I originally developed the games for my Forerunner 235.  

However, with newer devices - specifically, the Venu and the Forerunner 55 - this does not work.  For some reason, the display gets blacked out on every call to View.onUpdate. This only happens on the physical watches, not in the simulator, so I have no way to test this behaviour!I can create the undesired behaviour in the simulator by adding a call to View.onUpdate(dc) at the top of the onUpdate method in my view class.

So, here are my requests:

* please correct the behaviour on these watches

* if this behaviour was a desired change, please update your documentation to make it clear that we can no longer perform incremental updates to the screen

* please fix the simulator so that it behaves the same as these watches

* in the meantime, please tell me if there is a workaround for this issue.  I believe my code is correct since it has behaved correctly for years, and there is no detail in the documentation that says that what I'm doing is no longer possible

More details available on request - for some reason I can't add an image here.

  • Here's a workaround that allows you to draw via BufferedBitmap for those devices that need it.

    Add the following definition to your view class:

    	var bufferedBitmap = null;
    

    Add the following to the onLayout method of your view class:

    		if (Gfx has :BufferedBitmap) {
    		    // fr55 and similar need this kind of bitmap
    			bufferedBitmap = new Gfx.BufferedBitmap({
    				:width=>dc.getWidth(),
    				:height=>dc.getHeight(),
    				:palette=>[Graphics.COLOR_BLACK,
    							Graphics.COLOR_WHITE,
    							Graphics.COLOR_RED,
    							Graphics.COLOR_GREEN] // replace this array with the colours you need
    			});
    			try {
    				bufferedBitmap.getDc().setColor(Gfx.COLOR_WHITE, Gfx.COLOR_WHITE);
    				bufferedBitmap.getDc().drawText(dc.getWidth() / 2, dc.getHeight() / 2, Gfx.FONT_SMALL, ".", Gfx.TEXT_JUSTIFY_CENTER);
    			} catch (exception) {
    			    // fenix5 series can't draw antialiased fonts so need this kind of BufferedBitmap
    			    // (note: this method does not work with fr55)
    				bufferedBitmap = new Gfx.BufferedBitmap({
    					:width=>dc.getWidth(),
    					:height=>dc.getHeight()
    				});
    			}
    		}

    and replace the method definition of onUpdate with this code, so that your draw logic gets moved into a new method drawAllTheThings, either buffered or unbuffered:

       function onUpdate(dc) {
    		if (bufferedBitmap != null) {
    			drawAllTheThings(bufferedBitmap.getDc());
    			dc.drawBitmap(0, 0, bufferedBitmap);
    		} else {
    			drawAllTheThings(dc);
    		}
    	}
    
    	function drawAllTheThings(dc) {

    I believe we shouldn't have to do that kind of thing.  Monkey C should provide ways of drawing things that work on all devices, and the compiler should figure out what it has to do to make it work universally.  Right now, because this is device specific behaviour, it's not enough to rely on the simulator, and testing on physical devices is required.  That makes life hard for hobbyist developers, who don't have the time or money to invest in that.

  • BufferedBitmap is good when drawing picture doesn't change often else it bad idea - cost increases almost twice because first you have to draw in memory and than copy BMP to screen and if you additionally have to create bitmap to change size//palette is very bad.

    Recently I've noticed that View.onUpdate() is faster then dc.clear() but there is another problem after View.onUpdate() background is always black (View.onUpdate() doesn't honore previous call dc.setColor so if you have white background you have to clear dc again)

  • I have only dc calls, no layout.

    I haven't looked at using BufferedBitmap but if those render to the screen quickly, I guess it would be an option to have the onUpdate() logic first check if BufferedBitmap is present, and if so, draw to the bitmap and render that to the screen; if not, draw directly to the screen as previously.  That seems like it would be a relatively low-pain way to fix the issue without completely changing all my code, since the BufferedBitmap also provides a Dc instance.

  • of course it's the bug, somewhere, device or simulator, both should run the same way

    and yes, if something is by design should be described in documentation to this time it's a bug, to every bug report Garmin can answer - by design but if it's true, put this information in documentation, everybody will know why he has know all about jungle file, each device is different and there is no common sdk

    my post was connected rather z mips because I was surprised with different running my app on vivio4 - just clearing screen and I have to write everything even I don't have to do it and waste battery

    here is rather problem with amoled when clearing = taking off energy from pixel

    of course dc.clear is simple BUT UNFORTUNATELY COST A LOT OF TIME AND ENERGY

    and please to fix this bug on device or simulator

  • If you are using dc calls and not layouts, you actually don't need to all View.onUpdate() at all.  If you are mixing a lawout with dc calls, you need to do the View.onUpdate() before you do dc calls.  View.onUpdate() clears the screen so any dc calls before that are lost.

    The simple thing with only dc calls is to do the clear in onUpdate() and always redraw the whole screen.

    Have you looked at using buffered bitmaps to speed up your draws?