user5682282
user5682282

Reputation:

Android: Memory-optimized communication between Activity and Renderer (GLSurfaceView)

I'm currently working on an Android game and ran into a problem regarding communication between Activity and Renderer.

The Scenario

I have an Activity with a GLSurfaceView as the content view and a Renderer that is attached to GLSurfaceView. The Renderer contains the game loop. It draws the scene and updates the game state. This seems to be best practice and works smooth so far. The Activity additionally displays UI elements like buttons or the game score.

The Activity runs in the UI thread and Renderer has its own thread. So basically I have to deal with inter-thread communication. I use GLSurfaceView.queueEvent(Runnable) to call something in the Renderer thread from the Activity (e.g. starting the game loop). To call something in UI thread (e.g. updating game score UI element) from the Renderer I use Activity.runOnUiThread(Runnable).

The problem

Every time I want to communicate with the other thread a Runnable must be instantiated. At some point this will trigger Garbage Collection because the VM needs to free memory. This of course will interrupt the game.

Question

Are there any alternatives without instantiating new objects? The goal is to keep the memory allocation constant while the game is running.

Upvotes: 2

Views: 398

Answers (2)

Gergely Kőrössy
Gergely Kőrössy

Reputation: 6393

I wouldn't mix Android UI elements with the GLSurfaceView. Why, you might ask. Well, the normal UI elements use OGL as a best-effort. If there are certain things that cannot be rendered using OGL, they will be rendered otherwise (software render for example). Also, rendering those elements are subject to the lifecycle of the Activity render loop, thus effectively throttling your game's performance.

There are certain things you can do if you want to minimize the GC runs / queueEvent calls.

Go native

In C++ you allocate your own things and free them whenever you want. The native code is usually used for game physics and stuff like that but you can put OpenGL into it, too.

Change your model

Like I said, I wouldn't use the normal UI elements but render my own ones using textures (sprites) in OGL, thus eliminating the need for runOnUiThread.

As for the queueEvent, that's only needed if your invoked code modifies the OGL state machine (so basically all the gl* calls). My guess is that you mainly use this event queuing for passing touch coordinates and other "modifiers" to your game logic. In order to get rid of this, instead of modifying OGL directly from the invoked method, change only your model attributes and draw / set the new OGL state in the next render loop (make sure you make changes in a synchronized way, otherwise it can mess up things).

Use Canvas entirely instead of OpenGL

If your game is not graphics heavy and does not need special rendering options, you can use a canvas of a custom View which will allow you to render texts as well. Note that canvas might use OGL in the end for a lots of things, so for creating casual 2D games, it's still good.


Keep in mind...

Calling queueEvent with new Runnable instances is not necessarily a bad thing. The GLThread, that runs the Runnable instance, nullifies it (sets it to null) after taking it from the queue and running, which tells the GC that it can free its memory anytime. This is an optimization technique used widely in Java programming because the GC will not have to test certain things about this object anymore. Thus, the GC runs will not take as much time as you think.

Upvotes: 1

fadden
fadden

Reputation: 52313

Don't allocate Runnables for every message. Create a Handler for the thread receiving the messages, and get your Message objects with e.g. obtainMessage() to avoid allocations.

You can find multiple examples of this in Grafika, which takes steps to minimize or eliminate allocations across the various activities. Android Breakout has no[*] allocations during gameplay (though it doesn't use any View UI while playing).

Recommendations to switch to native code are generally overkill, especially if your only intent is to avoid garbage collection.

The best tool for evaluating the state of your game is the DDMS allocation tracker, which shows the N most recent allocations.

[*] I haven't checked Android Breakout in a while. The goal was zero allocations.

Upvotes: 2

Related Questions