My own rants and thoughts aloud about Symbian Platform

Collection of the random thoughts about the platform..

Modal Dialogs and the Game Loop

Modal dialogs are well known, highly utilized and mostly abused in the GUI world. Most of the time modality of the dialog covers two meanings:

  1. Modality of the interface from the user’s point of view.
    In the other words user is forced to make a choice before continue to work with the application
  2. Blocking the execution from the programmer’s point of view.
    That is, execution on current path is blocked untill dialog’s Execute method (or something similar) returns a result
To clarify the things out, need to mention what mode means a state, and thus its a statefull dialogs or in other words dialogs preserving a state as opposing to the the stateless dialogs which operates in parallel.

The former aspect of the modal dialogs is quite useful in many usage cases (altough I bet there are many usability researchers who frown at the sight of the dialog popping out of nowhere and basically forcing user to do something). However the latter aspect can be seen as the implementation artifact. While its simplifies programming approach, its also may cause major interferance with the other application functions which supposed to work at the time when dialog is displayed. It might be viable in the purely GUI applications (or otherwise such application can offload background tasks to the worker thread without a hassle) but in case of the real time games where many things executed sequentially in the single game loop it could create big big problem.

Problem outline

The problem arise from the common (or should I say “windows” inspired?) implementation of the blocking modal dialog boxes. Although the implementation is pretty straightforward and easy to comperhand, it has several inheritent flaws which make it undesirable especially in the game gui.

MS Windows GUI (as well as most of the MS GUI Libraries) implements modal dialogs by nested dispatcher loop, which effectively routes messages to the application’s message loop and at the same time blocks execution until the dialog is dismissed. Nesting message loop basically makes a nasty recursion in the execution path, something like in this scheme:

Level Routine
1 App::Main
2 App::RunLoop
3 OS:MessageDispatch
4 App::MessageHandler
5 App::SpecificMessageHandler
6 OS::ExecuteModalDialog
7 OS::MessageLoop
8 OS::MessageDispatch
9 App::MessageHandler

While game (PH in our case) avoids recursion by decoupling OS message processing and its own message loop, it still requires calling OS message dispatching cycle “from within”. Infortunately this approach is discouraged / prohibited by design of the Symbian GUI libraries.

Possible solutions

Nested Loop (Scheduler)

Thats the mostly straighforward conversion of the current approach. However there are many tricky places in the way how Symbian asynchronous events are scheduled and executed (comparing to the windows message handing) and actually platform documentation strongly recommends against using nested loops.

SDL solution

However, looks like Symbian port of the SDL works that way. Didn’t understand it well, actually. In rough outline it looks like it starts main thread with the single shot async callback and then periodically activates nested ActiveScheduler for system message dispatch. The PumpEvents procedure looks like that:

  • Enqueue low priority (that is, serves last in the queue) single shot callback into the Scheduler queue
  • Starts Scheduler (actually blocks in it)
  • As soon as scheduler process all previously enqueued requests (like GUI and input events) it reaches the callback we put previously
  • Callback checks if we lost a focus (switched out from the application window) and
  • if we lost focus: set a timer which waits 0.25s in loop and shutdowns Scheduler if focus is regained
  • otherwise shutdown Scheduler and flee out of the proc
  • does some hacky direct screen update

FSM decomposition

Its quite clear what modal dialogs could be emulated by decomposing execution flow into two states: in dialog and outside of dialog i.e. instead of calling “execute modal” and blocking in the call we can create dialog widget, set some global state flag and bail out. As soon as the execution path enter new game (or GUI) cycle it will serve dialog’s logic instead of ours. Then as the dialog interaction finishes, it sets that global flag to the new, destination state, effectively redirecting execution to our new entry point.

Its easy to see what, although logically correct this solution is foreign with respect to the c++ programming - having no any effecitve ways for FSM decomposition it dooms programming to splitting otherwise logicically continious program flow into separate parts (which also should carefuly manage the shared state)

<need an example>

  1. ProcessTick()
    1. ProcessGame( state1 )
      1. Call dialog (create, set new state, bail out)
  1. ProcessTick()
    1. ProcessGame( dialog state )
      1. Handle dialog input etc
        1. if done, (set state to state2, share data, bail out)
  1. ProcessTick()
    1. ProcessGame( state2 )
      1. something what was waiting for dialog results

Greet (cooperative) threading

Clean, although mostly unsupported in C++ environment method.

Sets “modal dialog flag”, yields a thread on “executeModal”. Global management thread will check a flag and execute dialog code (not allowing to invoke caller thread) until it exist.

Deferred calls

Dialog executes in non-blocking fashion. On exit it enqueues “async callback” with its result to the message queue so its served as soon as we come back to the message loop.

Continuations

Similar to the green threads but even more unnatural to the C++. However if we move to the scripting game core it might be easility enrolled on top of Lua or Squirel language.

Separate threads

Dialog starts separate thread, main thread free to choose: block in waiting for dialog thread or continue exection and block later.

Dialogs as futures

If we thread dialog result as Future (see multithreading ideoms).. it brings out back recursively to the same problem (in case if we access future within same cycle)

Relevant links, literature

 
on_symbian_uiq3_porting.txt · Last modified: 2008.03.28 04:03 by admin
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki