BACKGROUND PROCESSING
An application's interface should always remain as responsive to a user
as possible. This requires that:
- Startup and foreground processing need to be very quick
- Anything lengthy should be done in background
- Any background processing must not lock out the foreground for
more than a small fraction of a second
- Even if the background processing must continue for a long time
uninterrupted, it should at least arrange to process expose events.
There are several possible ways to do background processing:
- Use one or more workproc's to do processing whenever there are no
pending X events. Just don't stay in the workproc more than a
fraction of a second without processing events (see below).
- Use XtAppAddTimeout() to call function(s) on a regular basis.
You might want to do this instead of using a workproc if your
background processing is more at regular intervals than continuous.
- Process from the application's main-line code. In this case, the
application must do its own regular event polling and dispatching.
Xt is event driven, not interrupt driven. No background processing,
whether it be a workproc, a timeout routine, or just ordinary code,
will be interrupted by X. If the background processing really must run
a long time, then it should voluntarily relinquish control from time to
time to ensure that events get serviced frequently. If it does not do so,
processing user input will be delayed.
If you are using either workproc(s) or timeout routine(s), and if no one
of them is longer than a small fraction of a second long, then event
handling will be responsive without taking any special action.
Keeping a application responsive, even if needs time-consuming
background processing, need not be too restrictive. It just means that
the background processing must be properly structured. One way to think
of this is that is is the reverse of the usual -- instead of relying on
Xt to call the application from time to time for a little processing,
the application calls Xt from time to time to let Xt do a little
processing. There are several ways this can be done:
- You can use a workproc that at frequent intervals notes its own
state and returns FALSE. That will let any pending events be
serviced, and then the workproc will be called again. When the
workproc is finally done, it needs to return TRUE so it will not
get called again.
Note that workproc's are called in order. If you want several
workproc's to get time in any other manner, you must arrange that
yourself.
- You can use a timeout routine that at frequent intervals notes
its own state, sets another timeout, and returns. That will let
any pending events be serviced, and then when the timer goes off
the routine will be called again.
Note that timeout's are higher priority than workproc's.
No workproc will get run unless there are no pending events
and also no expired timeouts. Thus, if your timeout routine
set another timeout, and if it times out before the routine exits,
events will get processed but no workproc will ever get called.
Doing this is one way to get events serviced during a long timeout
routine. It in effect turns your timeout routine into the
highest priority workproc. If you want a real workproc called
before such a timeout routine is done, the timeout routine must
do that itself.
- In some cases, it will be difficult (or impossible) to
keep track of state, exit, and be restarted. An example
of this might be a lengthy recursive algorithm.
In that case, the background processing needs to do its own
regular polling and event dispatching. When to do the polling
can be determined in any of a number of ways, such as:
- algorithmically (perhaps each time through the process's
main loop
- after each significant section of the background task
- poll from a timeout routine.
If a workproc must be called, it is up to the background processing
to do so itself.
OVERVIEW OF WORKPROC'S
- Xt provides a limited form of background processing, the
XtWorkProc. An XtWorkProc is called when there are no events
pending. An application may have more than one workproc.
- Register each workproc, using XtAppAddWorkProc(). The easiest
way to preserve interactive response may be to register more than
one workproc.
- If a workproc is to be run at any time other than the standard
(in order, and only when no events are pending), the application
must call the workproc(s) directly.
- There is no non-blocking way to tell that a workproc has been
registered. If this knowledge matters to an application, the app
must keep track of it itself.
- Workproc's are unrelated to X events, and are of lower priority.
The only connection is that a workproc is called from the X event
loop when there are no X events to service. Once a workproc is
called, nothing else will run until the workproc returns.
- When a workproc returns:
- any pending events will be serviced
- if the workproc returned FALSE, it will be run again
- if the workproc returned TRUE, it will be un-registered,
and the next workproc (if any) will be run.
- No workproc runs until all higher priority workproc's have
completed. If your application needs anything else, it will
have to do its own scheduling (instead of depending on Xt). In
such a case, it will not register most or all of its workproc's.
It will just arrange to call them itself.
- For more than one workproc, there is a defined order in which
they will be called. One will not be run until all higher
priority workproc's have been run to completion (i.e. returned
TRUE).
- For workproc's registered when not already running a
workproc, the last registered is the highest priority.
- workproc's registered from within a workproc are lower
priority than that workproc.
- You might use more than one workproc if:
- Putting clearly separate tasks in distinct workproc's
is the cleanest way to write the application.
For example, if your initialization needs to create
several dialog windows, you could register several
workproc's to do so. Note that this could be the same
workproc each time, perhaps with different client data.
The workproc would keep track of which action to perform
each time either by the different client data argument,
or by keeping its own internal static records.
- One long workproc task can be broken up into a number
of acceptably short tasks. By putting them in separate
workproc's, you save the trouble of ensuring that the
workproc periodically returns to the X event loop.
If you keep all in one workproc, you are responsible
for preserving interactivity -- by using polling or
timeouts or whatever else to ensure that the one workproc
doesn't hog the cpu for too long.
POLLING FOR EVENTS
Long-running background tasks should poll for, and process, events.
At a minimum, the process should honor expose events. If expose events
are all that is being processed, the application should put up a busy
cursor.
Following is a code sample that will poll for and process all events.
Note that if you are in a callback and there is a pending event that would
trigger the callback, you will get re-entered. If you are in a timeout
routine and another timer for that routine has gone off, you will also
get re-entered. Your application is responsible for handling such a
case. Of course, you can avoid the problem by keeping your callbacks and
timeout routines short enough that polling is not necessary.
while (XtAppPending(appContext))
XtAppProcessEvent(appContext, XtIMAll);
The manual pages describe these functions and their parameters in more
detail. You can do such things as process only specific events. This
document is only an overview. For details, read the appropriate
sections from the following references:
* "X WINDOW SYSTEM TOOLKIT", by Asente & Swick
PART I PROGRAMMER'S GUIDE
section 6.8 "Getting Events"
6.9 "Dispatching Events"
6.10 "Custom Event-Dispatching Loops"
6.11 "Background Work Procedures"
6.11 "Using Xlib Event Routines"
PART II SPECIFICATION
section 7.4 "Querying Event Sources"
7.5 "Dispatching Events"
7.6 "The Application Input Loop"
7.8 "Adding Background Work Procedures"
* "X Toolkit Intrinsics Programming Manual, OSF/Motif 1.2
Edition", (O'Reilly Vol 4)
section 9.6 "Work Procedures"
* "X Toolkit Intrinsics Reference Manual", (O'Reilly Vol 5)
XtWorkProc
XtAppAddWorkProc()
XtRemoveWorkProc()
XtAppPending()
XtAppNextEvent()
XtAppProcessEvent()
* "Motif Programming Manual" (O'Reilly Vol 6)
section 20.2 "Working Dialogs"
20.2.1 "Using Work Procedures"
20.2.2 "Using Timers"
20.2.3 "Processing Events Yourself"
* "The X Window System, Programming and Applications with Xt,
OSF/Motif Edition", by Doug Young
section 5.7 "USING WORKPROCS"
NON_TOOLKIT X PERFORMANCE NOTES
- Don't needlessly complicate window clipping. For example:
+----------+
| |
| A +-----------+
| | |
+------| B |
| |
+-----------+
Drawing performance to window A is reduced because it doesn't
have a single rectangular clip. For toplevel windows, the way
windows are overlapped is up to the user via the window manager
but inside your application hierarchy, it should avoided.
- If you use a GC to draw to both a pixmap and a window, you pay the
price of a full GC validation every time you switch between the pixmap
and the window on SGI machines (the code to draw to the graphics
hardware is totally different from the dumb frame buffer hardware
used for pixmaps and so the hooks in the GC must be totally revalidated
each time you switch between a window and a GC).
This should not be a key performance issue but if you have code like:
create window A
create pixmap B
create GC C
repeat alot of times
do some primtive to A using C
do some primtive to B using C
The code will be faster if you use:
create window A
create window B
create GC C
create GC D (the same as C)
repeat alot of times
do some primitive to A using C
do some primitive to B using D
- The SGI X server does allow you to run with a default depth other than
8 (the default) using the -depth option (see Xsgi man page). Most X
clients use the default visual. This means that all of their pixmaps
will of the default depth. A pixmap of depth 12 takes twice the memory
of a pixmap of depth 8. A pixmap of depth 24 takes four times the memory
of a pixmap of depth 8. Also the rendering code to these pixmaps is
slower.
If you need an X client to use a 24-bit TrueColor visual, the best
thing is to right that program to find such a visual instead of
reconfiguring the X server to use a 24-bit TrueColor visual.
- Compress expose events. Naive redraws will not only be visually
unattractive but also waste the X servers time. Motif and Xt do this
for you.
BACKING STORE and SAVE UNDER
Backing store and save under are intended as performance optimizations.
In practice, using them may instead lead to performance degradation.
Backing store and save under for non-GL windows
- Backing store and save under will never work for IrisGL
clients. That is, IrisGL pixels will not be saved and
restored.
- Backing store and save under will never work for OpenGL that is
rendered directly (rather than through the X server).
- Backing store and save under may possibly work in a future
release, for OpenGL that is rendered through the X server.
However, that is definitely not currently committed and may never
happen.
- The difference in these three cases is the ability to render
to pixmaps, based on the window's clip.
- IrisGL will never render to pixmaps
- Direct-rendered OpenGL clients have neither knowledge of
the window's clip, nor the tolerance for any additional
level of indirection that would affect rendering performance.
- Indirectly rendered OpenGL clients will share the server's
knowledge of the clip and can tolerate the extra level of
indirection needed to decide whether to render to pixmap
or to the screen
Backing store and save under for non-GL windows
- Most Xt apps may be better off without using either backing store
or save unders.
- Consider a control panel with a few buttons and a few strings.
Backing store is almost certainly a major lose in 99+% of
configurations, because rendering a few rectangles and a little
text should be much faster than saving and restoring the affected
pixels.
- On the other hand, if you have a very expensive, difficult
rendering to do, and you're essentially blitting pixels anyway,
then backing store can
- prevent you from having to recalculate (if your app is
dumb enough to have to recalculate)
- prevent the pixels from having to go over the net.
- The app developer must understand the backing store implementation
and think the performance issues through.
One place that backing store contributes to poor perceived performance
is using window managers with opaque move enabled (mwm and 4Dwm support
opaque move). When you move an opaque window over a backing store window,
you are asking the server to do lots of backing store operations. The
result can be very sluggish opaque moves.
If clients handle their own redraws, the movement can be much more fluid
because clients catch up on exposes and can compress them. The visual
effect is not nearly as jerky.
TODO:
- Test gadget creation speed. Perhaps this would make a good
demo.
SGI has some constraints in what we can do with/to the Motif library:
- Motif itself is constrained by Xt and X11.
- Motif is a standard, so we can only do things that will
not change any application-visible behavior.
- We need to merge new releases of Motif several times per year.
We cannot make changes that are on a scale that will prevent
us keeping up with the standard.