You say: "So, say I have the game loop logic in a thread like that,
using the
render fps average as a counter, I've noticed that the fps drops when
using more than one thread, even if it's just running in an empty
loop. "
Um, ESPECIALLY if it's running in an empty loop. The point is, you
don't want it to run AT ALL unless it has something to do.
Handler is one way to do this. Sleep never helps you do this, UNLESS
"something to do" is purely based on time.
What your code below is doing is called "polling". Polling is the
technique of last resort, because your thread has to wake up
periodically even if there's nothing to do.
That may not be serious -- and sometimes it's your only choice. But
the ideal is to block, waiting to be told there's actual work to do.
"I remember timing this was I would constantly send network data to
the
network thread with a timestamp, then I would check how long it took
for that message to make it to the game loop by determining the time
difference. "
This doesn't sound like a good way to measure, as there are too many
variables.
If you created the timestamp at the point of sending it to the
handler, and how long it took before the handler began executing,
you'd have a valid measurement of the latency. However, even latency
is not telling you efficiency.
Using a Handler does not involve creating a thread, so I'm not sure
why you mention that you use a thread pool. Indeed, if you're using
multiple threads to do intermittent work, a thread pool is a good
idea. However, since you have only one main processor, if you're doing
work that doesn't block, you may find no advantage, and even a small
performance DECREASE, by having more than one thread in your pool!
(There can be other reasons to have more than one thread, however; I'm
just discussing performance).
Handler is presumably doing something like this to queue:
synchronized (m_actions) {
m_actions.add(r);
m_actions.notify();
}
And something like this to pull from the queue and execute:
while (true) {
Runnable action = null;
synchronized (m_actions) {
while (m_actions.isEmpty()) {
try {
m_actions.wait();
} catch (InterruptedException e) {
}
}
action = m_actions.remove();
}
action.run();
}
When there's nothing to do, the thread is waiting in the wait() call,
not running, not consuming any CPU. (It does consume some significant
memory; idle threads are not free!)
(Don't worry about that inner loop and catch of InterruptedException
-- nobody will be interrupting, that's a holdover from the early days
of Java).
When something is placed in m_actions, the thread running the loop
*immediately* becomes runnable. Whether it runs immediately depends on
whether it has to compete with other threads which are also runnable.
And more importantly, if it was NOT in the wait() call -- that is, it
is still running an earlier request, then it will not run the new
runnable until the old one completes, and it goes around the loop
again to check.
This loop NEVER runs unless there is work to do -- yet it's
immediately runnable when there is. (The exception is that it checks
if something is already queued on first entry). This is the ideal.
With polling, you have to check if there's work to do. The check here
IS run before waiting after just completing work, but the thread was
already awake in this case. It never wakes up just to see if there's
work.
"The game loop I'm using is SUPPOSED to be 60fps but will undoubtedly
vary, I use the Timer class's scheduleAtFixedRate to automatically
take care of the timestep adjustments/wait time.
Since the timer class uses a thread pool, it doesn't have an overhead
of thread creation. I don't have to handle calculating how long to
sleep to maintain a constant rate, and is quite efficient.
I've achieved a smooth display, and the physics progresses a step at
each game loop iteration.
This has worked well for me. "
If that's all worked well for you, I'm NOT telling you that you need
to change it. However, there's a number of ways this can break down
when you really push the limits.
But a guiding principle is -- Do The Simplest Thing That Could
Possibly Work. It sounds like you've achieved that, for now. Or maybe
"The First Thing That Worked", and sometimes that's a good choice,
too! :=)
If someday you find it no longer meeting your needs, you can take
these other things into consideration. Or others reading this thread
may find them useful.
On Apr 10, 6:34 pm, Miguel Morales <[email protected]> wrote:
> Ok, like I said I may have missed something, but say I have something like
> this:
>
> class MyThread extends Thread
> {
> run()
> {
> while (something)
> {
> if (something_else) //perhaps input has changed, or we have
> data in a socket
> {
> //do some work, not idle
> }
> else
> {
> //no work to do
> }
> Thread.sleep(x); //maybe needed, based on implementation
> }
> }
>
> }
>
> So, say I have the game loop logic in a thread like that, using the
> render fps average as a counter, I've noticed that the fps drops when
> using more than one thread, even if it's just running in an empty
> loop.
>
> Using a looper object forces me to communicate with the thread by
> posting messages to its handler. Say I have a network thread that
> just got a message and wants to let the game loop of a new data
> available, or the UI thread wants to let the game loop know that the
> input state has changed. It can be done using a handler, or using a
> lock to protect a data buffer.
>
> So say in the game logic loop we constantly check the state of some
> variable and doing work accordingly, I have found it's much faster to
> use a lock around an object than it is to post to a handler. The way
> I remember timing this was I would constantly send network data to the
> network thread with a timestamp, then I would check how long it took
> for that message to make it to the game loop by determining the time
> difference.
>
> I was working directly with bytes, as to avoid any sort of
> serialization overhead.
>
> The game loop I'm using is SUPPOSED to be 60fps but will undoubtedly
> vary, I use the Timer class's scheduleAtFixedRate to automatically
> take care of the timestep adjustments/wait time.
>
> Since the timer class uses a thread pool, it doesn't have an overhead
> of thread creation. I don't have to handle calculating how long to
> sleep to maintain a constant rate, and is quite efficient.
>
> I've achieved a smooth display, and the physics progresses a step at
> each game loop iteration.
> This has worked well for me.
>
>
>
> On Sat, Apr 10, 2010 at 12:57 PM, Bob Kerns <[email protected]> wrote:
> > If a thread is hogging the CPU, it is *BY DEFINITION* not idle. And
> > vice versa.
>
> > An idle thread will be waiting on something. Either a synchronization
> > lock, or an object using wait(), or some blocking IO call. This may be
> > buried inside some other code, of course.
>
> > I would suggest wondering why you thought this. How are you
> > determining what threads are consuming what CPU? Either you have some
> > sort of methodology error, or interpretation error.
>
> > I would wonder about your statement about a Looper and Handler being
> > slow. I haven't read the code, but the implementation I would expect
> > would be very simple, and using wait/notify. In other words, they
> > SHOULD be fast. If you find them slow, I'd again check your
> > methodology -- and if you can confirm, I'd suggest filing a bug
> > report, since that's something that could be improved and would
> > improve things for the entire platform.
>
> > Re 60 fps game logic -- some alternatives to consider that may be
> > better choices in some circumstances. I'm only considering physics
> > here; I'm not sure what the time constraints on other game logic might
> > be:
>
> > If you're doing your physics calculations correctly, you won't be
> > constrained to a constant game logic rate. You may be able to vary
> > your simulation time step to maintain a more steady render rate (at
> > the cost of some accuracy), and even vary it between different parts
> > of the model, e.g. critical portions (e.g. collisions) run a higher
> > rate, while running a longer timestep for portions not subject to time-
> > varying forces.
>
> > If smooth display is more important than physical accuracy, running
> > physics after each frame, driven by actual elapsed time, may be the
> > way to go. You can trigger that as you're about to flip the scene, so
> > it overlaps the GPU. You may prioritize your fast-moving stuff, and
> > only update a portion of your model on each frame.
>
> > Also, you may be able to apply Level of Detail (LOD) control to your
> > scene -- including fewer polygons when the overall frame rate drops
> > (due to either graphics or physics).
>
> > All that is a lot more complex, of course, but may let you push the
> > envelope a bit further.
>
> > On Apr 9, 7:42 pm, Miguel Morales <[email protected]> wrote:
> >> I may be completely wrong on this, but I've found that threads hog the
> >> CPU even when idle.
>
> >> The way that has worked well for me is to have the render thread, and
> >> the logic timer.
>
> >> (using a looper and communicating via a handler can be slow, from my
> >> experience locks are faster)
>
> >> To keep things simply I use a TimerTask to simulate a 60fps game logic
> >> rate (game_loop_timer.schedule(game_loop_task, 0, 17);). The game
> >> logic rate can vary based on what needs to be done. (i.e pathfinding,
> >> network syncing, etc.) and sets a scene to the buffer.
>
> >> The render thread stays constant by simply drawing what's on the scene
> >> buffer at every onDraw.
>
> >> I use a single lock to keep the scene buffer safe between the two threads.
>
> >> It's actually pretty simple, and I only really need two classes.
>
> >> I highly recommend reading articles about this, here are some of my
> >> bookmarks:http://gafferongames.com/game-physics/fix-your-timestep/http://stacko......
>
> >> For input, I use a lock for the state of the hardware. (i.e. when the
> >> screen is being touched, I set a boolean isTouched to true and use a
> >> lock to safely read/write from the threads. However, a queue/handler
> >> is really the way to go when it comes to that since it's not as
> >> time/latency sensitive.
>
> >> On Fri, Apr 9, 2010 at 6:29 PM, Lance Nanek <[email protected]> wrote:
> >> > There are a lot of built in classes in Android and Java that you can
> >> > use to avoid having to write any synchronization or lock code
> >> > yourself. In my case GLSurfaceView sets up my render thread. I use
> >> > HandlerThread for my game thread. The game thread sends an update
> >> > object detailing all the draw commands needed for a frame to the
> >> > render thread via a BlockingQueue. The render thread returns it via
> >> > the game thread's Handler. The UI thread tells the game thread about
> >> > input via the game thread's Handler as well. These classes are in the
> >> > android.os and java.util.concurrent packages.
>
> >> > On Apr 9, 6:36 pm, Eddie Ringle <[email protected]> wrote:
> >> >> Okay, I figured I could just pass the Activity through parameters, but
> >> >> wanted to know if a better way was possible.
> >> >> Thanks for all the help everyone.
>
> >> >> On Apr 9, 5:56 pm, Mario Zechner <[email protected]> wrote:
>
> >> >> > That depends on what kind of input you need. If your game is happy
> >> >> > with just checking the current state of the accelerometer/touch
> >> >> > screen/
> >> >> > keyboard/trackball simply polling will do the trick. This means that
> >> >> > in the UI thread, where you have your event listeners installed, you
> >> >> > simply save the last state reported to you. In your logic thread you
> >> >> > can savely access this state even without synchronization as it will
> >> >> > only be composed of plain old data types like booleans or ints (of
> >> >> > course there are cases where you catch the x coordinate of the
> >> >> > previous event and the y coordinate of the current event but i dare
> >> >> > say that the differences are so tiny, it doesn't make a difference).
>
> >> >> > Other things might need event based input handling, like a GUI you
> >> >> > implement yourself with say OpenGL. In that case you will need a queue
> >> >> > that gets filled in the Android GUI thread by the listeners you have
> >> >> > installed for the input devices. The logic thread acts as a consumer
> >> >> > and takes the events from the queue. As you are working with a complex
> >> >> > data structure you need to synchronize the adding and removing of
> >> >> > events to and from the queue in both the Android GUI thread and your
> >> >> > logic thread. This is a classical consumer/producer pattern which can
> >> >> > be found frequently in multithreaded applications. Check out Robert's
> >> >> > site athttp://www.rbgrn.net, i think he wrote an article on that sort
> >> >> > of thing once.
>
> >> >> > it basicaly boils down to this (pseudo code, written from the top of
> >> >> > my head, will not compile)
>
> >> >> > public class GameActivity extends Activity implements
> >> >> > MotionEventListener
> >> >> > {
> >> >> > Queue<Event> events = new Queue<Event>();
>
> >> >> > public void onTouch( MotionEvent event )
> >> >> > {
> >> >> > synchronized( events )
> >> >> > {
> >> >> > events.add( new Event( event.getX(), event.getY() );
> >> >> > }
> >> >> > }
>
> >> >> > }
>
> >> >> > public class GameLogic
> >> >> > {
> >> >> > GameActivity activity;
>
> >> >> > public GameLogic( GameActivity activity )
> >> >> > {
> >> >> > this.activity = activity;
> >> >> > }
>
> >> >> > public void handleInput( )
> >> >> > {
> >> >> > synchronized( gameActivity.events )
> >> >> > {
> >> >> > // proces events here
> >> >> > }
> >> >> > }
>
> >> >> > }
>
> >> >> > Now, a couple of comments: You don't want to instantiate a new Event
> >> >> > everytime a listener method in the GameActivity is called. You will
> >> >> > need to implement a simple object pool and reuse events. That way the
> >> >> > garbage collector will stay calm. Also note that the design above is a
> >> >> > bit nasty, i would directly pass the GameActivity to the GameLogic
> >> >> > among other things. But that's up to you.
>
> >> >> > Polling input handling would work like above but without a queue and
> >> >> > without the need for the synchronized blocks. All you do is set some
> >> >> > members of GameActivity, say touchX and touchY in the
>
> ...
>
> read more »
--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en
To unsubscribe, reply using "remove me" as the subject.