Loading data as background process

I was wondering how others manage loading their large system data (images, etc), while displaying a progress bar?

I understand that I cannot load images in a thread other than the initial SDL main process, so I am at a loss as to how I can update a progress bar and still separately load image data. The only solution I could think of was to load a single image every loop, until all are loaded. That would of course work fine, but I’d like a nice generalised system where I didn’t have to have code that tracks which item I’m up to loading.

Any ideas?

Thanks,

Rob

I was wondering how others manage loading their large system data
(images, etc), while displaying a progress bar?

I understand that I cannot load images in a thread other than the
initial SDL main process, so I am at a loss as to how I can update
a progress bar and still separately load image data.

Well, I think you could, if you have a global lock or something that
must be held while using SDL surface manipulation calls from anywhere
in your code while loading. (Which basically means it affects only
the loader and the rendering code that’s used while loading.)

The only
solution I could think of was to load a single image every loop,
until all are loaded.

That’s what I do in Kobo Deluxe, and I think that’s the most common
solution. It’s simple and robust, though the code gets ugly if you
don’t have some kind of loading subsystem you can hook into…

As it is, loading is just a long list of calls, with progress(…);
calls in between each operation. If that part of the graphics engine
was slightly more sophisticated, I could just send it a list of files
and parameters, and the progress bar would be driven through a
callback from a loop somewhere inside the engine instead.

With a centralized approach like that, one could record the loading
time in some file to make the progress bar more accurate for
subsequent loads, and that sort of stuff.

That would of course work fine, but I’d like
a nice generalised system where I didn’t have to have code that
tracks which item I’m up to loading.

Any ideas?

You could try wrapping the relevant SDL calls with something that uses
a global lock to make sure that only one thread at a time is using
those calls. That way you can at least have the loader thread use s/w
blitters and stuff like that, while the main loop updates the
progress bar a few times a second or so.

Of course, there’s no way to avoid that progress logic, but at least,
this would avoid having the loader actually drive the progress bar
directly.

However, it would be much easier to just have those SDL call wrappers
update the progress bar directly, and run the loader in the main
thread.

It doesn’t really make a difference how you implement it, since you
can’t get “fractional file” resolution without explicit support in
the loader anyway.

//David Olofson - Programmer, Composer, Open Source Advocate

.- Audiality -----------------------------------------------.
| Free/Open Source audio engine for games and multimedia. |
| MIDI, modular synthesis, real time effects, scripting,… |
`-----------------------------------> http://audiality.org -’
http://olofson.nethttp://www.reologica.se —On Monday 31 May 2004 00.33, Rob Sadedin wrote:

Well, I think you could, if you have a global lock or something that
must be held while using SDL surface manipulation calls from anywhere
in your code while loading. (Which basically means it affects only
the loader and the rendering code that’s used while loading.)

I’m not sure if you can safely touch hardware surfaces from other threads.
I know that’ll give OpenGL a hernia in Windows (contexts are selected
per-thread and can only be selected into one thread); I don’t know if
DirectDraw can handle it.

That’s what I do in Kobo Deluxe, and I think that’s the most common
solution. It’s simple and robust, though the code gets ugly if you
don’t have some kind of loading subsystem you can hook into…

The approach I use is to pass a class base pointer to the long-running
loader. For initial loads, there’s a regular Windows dialog (in Windows,
at least), and the class implemention updates the text in the window
and draws it. For reloads, there’s an OpenGL or D3D window, and the
class renders a screen periodically. (This can be done in C somewhat
less elegantly by passing a callback.)

http://cvs.sf.net/viewcvs.py/stepmania/stepmania/src/arch/LoadingWindow/
http://cvs.sf.net/viewcvs.py/stepmania/stepmania/src/ScreenReloadSongs.cpp?view=markup
http://cvs.sf.net/viewcvs.py/stepmania/stepmania/src/SongManager.cpp?view=markup

You could try wrapping the relevant SDL calls with something that uses
a global lock to make sure that only one thread at a time is using
those calls. That way you can at least have the loader thread use s/w
blitters and stuff like that, while the main loop updates the
progress bar a few times a second or so.

If all you want is a progress bar, using a thread is extreme overkill.On Mon, May 31, 2004 at 01:00:51AM +0200, David Olofson wrote:


Glenn Maynard

Thanks for the feedback guys (David and Glenn),
I guess I’ll look at using a callback and track where I’m upto, as you’ve
suggested.
I don’t think I’ll look at actually tracking true average load time, but
simply go off an overall image count, which I suspect is what all the
commercial apps do anyway.
I don’t think I’ll even bother looking at messing with locking and trying to
access, because I’m not sure that under all platforms it would work.

Thanks again,

Rob> ----- Original Message -----

From: david@olofson.net (David Olofson)
To: “Rob Sadedin” <@Robert_Sadedin>; “A list for developers
using the SDL library. (includesSDL-announce)”
Sent: Monday, May 31, 2004 9:00 AM
Subject: Re: [SDL] Loading data as background process

On Monday 31 May 2004 00.33, Rob Sadedin wrote:

I was wondering how others manage loading their large system data
(images, etc), while displaying a progress bar?

I understand that I cannot load images in a thread other than the
initial SDL main process, so I am at a loss as to how I can update
a progress bar and still separately load image data.

Well, I think you could, if you have a global lock or something that
must be held while using SDL surface manipulation calls from anywhere
in your code while loading. (Which basically means it affects only
the loader and the rendering code that’s used while loading.)

The only
solution I could think of was to load a single image every loop,
until all are loaded.

That’s what I do in Kobo Deluxe, and I think that’s the most common
solution. It’s simple and robust, though the code gets ugly if you
don’t have some kind of loading subsystem you can hook into…

As it is, loading is just a long list of calls, with progress(…);
calls in between each operation. If that part of the graphics engine
was slightly more sophisticated, I could just send it a list of files
and parameters, and the progress bar would be driven through a
callback from a loop somewhere inside the engine instead.

With a centralized approach like that, one could record the loading
time in some file to make the progress bar more accurate for
subsequent loads, and that sort of stuff.

That would of course work fine, but I’d like
a nice generalised system where I didn’t have to have code that
tracks which item I’m up to loading.

Any ideas?

You could try wrapping the relevant SDL calls with something that uses
a global lock to make sure that only one thread at a time is using
those calls. That way you can at least have the loader thread use s/w
blitters and stuff like that, while the main loop updates the
progress bar a few times a second or so.

Of course, there’s no way to avoid that progress logic, but at least,
this would avoid having the loader actually drive the progress bar
directly.

However, it would be much easier to just have those SDL call wrappers
update the progress bar directly, and run the loader in the main
thread.

It doesn’t really make a difference how you implement it, since you
can’t get “fractional file” resolution without explicit support in
the loader anyway.

//David Olofson - Programmer, Composer, Open Source Advocate

.- Audiality -----------------------------------------------.
| Free/Open Source audio engine for games and multimedia. |
| MIDI, modular synthesis, real time effects, scripting,… |
`-----------------------------------> http://audiality.org -’
http://olofson.nethttp://www.reologica.se


SDL mailing list
SDL at libsdl.org
http://www.libsdl.org/mailman/listinfo/sdl

Well, I think you could, if you have a global lock or something
that must be held while using SDL surface manipulation calls from
anywhere in your code while loading. (Which basically means it
affects only the loader and the rendering code that’s used while
loading.)

I’m not sure if you can safely touch hardware surfaces from other
threads.

You should be able to touch VRAM directly from any thread on any h/w I
know of - but indeed, locking, accelerated blitting etc won’t work at
all in many cases, since the device context is often linked to the
thread that creates it.

I know that’ll give OpenGL a hernia in Windows (contexts
are selected per-thread and can only be selected into one thread);

I think that applies to most platforms to some extent, since the idea
is that professional 3D apps should be able to use multiple contexts
at once from different threads. (If you can’t do that, you’ll have
serious trouble keeping the GUI responsive.)

[…]

You could try wrapping the relevant SDL calls with something that
uses a global lock to make sure that only one thread at a time is
using those calls. That way you can at least have the loader
thread use s/w blitters and stuff like that, while the main loop
updates the progress bar a few times a second or so.

If all you want is a progress bar, using a thread is extreme
overkill.

Well, it could let you do some silly animations and stuff while the
loader is doing CPU heavy processing and stuff… Whether that’s
worth the effort or not is a matter of personal taste. :slight_smile:

//David Olofson - Programmer, Composer, Open Source Advocate

.- Audiality -----------------------------------------------.
| Free/Open Source audio engine for games and multimedia. |
| MIDI, modular synthesis, real time effects, scripting,… |
`-----------------------------------> http://audiality.org -’
http://olofson.nethttp://www.reologica.se —On Monday 31 May 2004 01.23, Glenn Maynard wrote:

On Mon, May 31, 2004 at 01:00:51AM +0200, David Olofson wrote:

Thanks for the feedback guys (David and Glenn),
I guess I’ll look at using a callback and track where I’m upto, as
you’ve suggested.
I don’t think I’ll look at actually tracking true average load
time, but simply go off an overall image count, which I suspect is
what all the commercial apps do anyway.

Yeah…

Looking at the file sizes might give you a pretty good approximation,
though. You’ll need the full file list to calculate the total size
first, but you pretty much need that anyway, for the file count.
(Unless you just hardcode the whole thing like I did in Kobo Deluxe.
Wouldn’t recommend that for anything that has more than 5-10 files to
load…)

I don’t think I’ll even bother looking at messing with locking and
trying to access, because I’m not sure that under all platforms it
would work.

I think it should work as long as you’re using the SDL s/w blitters
and s/w surfaces only in the loader thread.

However, I think we’ve concluded that it’s not a good idea, unless you
actually have work to do while the loader thread is working on
individual files. It’s safer and much easier to just not bother with
that stuff.

//David Olofson - Programmer, Composer, Open Source Advocate

.- Audiality -----------------------------------------------.
| Free/Open Source audio engine for games and multimedia. |
| MIDI, modular synthesis, real time effects, scripting,… |
`-----------------------------------> http://audiality.org -’
http://olofson.nethttp://www.reologica.se —On Monday 31 May 2004 02.16, Rob Sadedin wrote:

I’m not sure if you can safely touch hardware surfaces from other
threads.

You should be able to touch VRAM directly from any thread on any h/w I
know of - but indeed, locking, accelerated blitting etc won’t work at
all in many cases, since the device context is often linked to the
thread that creates it.

It’s more driver and/or API limitations causing problems, not the
hardware. After all, the video hardware couldn’t care less about
threads.

The OpenGL example is relevant to glSDL, too.

I know that’ll give OpenGL a hernia in Windows (contexts
are selected per-thread and can only be selected into one thread);

I think that applies to most platforms to some extent, since the idea
is that professional 3D apps should be able to use multiple contexts
at once from different threads. (If you can’t do that, you’ll have
serious trouble keeping the GUI responsive.)

Direct3D allows accessing the same context from multiple threads, which
is very useful, since it lets me update streaming movie textures in a
thread, without having to synchronize (as long as I arrange for the texture
to not be in use at the time). That’s impossible with Windows’s OpenGL
implementation.On Mon, May 31, 2004 at 04:56:56AM +0200, David Olofson wrote:


Glenn Maynard

I’m not sure if you can safely touch hardware surfaces from
other threads.

You should be able to touch VRAM directly from any thread on any
h/w I know of - but indeed, locking, accelerated blitting etc
won’t work at all in many cases, since the device context is
often linked to the thread that creates it.

It’s more driver and/or API limitations causing problems, not the
hardware. After all, the video hardware couldn’t care less about
threads.

Right, but drivers generally won’t (or can’t) care which thread does
the direct VRAM access once the VRAM is mapped into the address space
of the process. So, if you actually have h/w surfaces, you could
render into them from multiple threads, provided all locking and
other API/driver calls are done from the main thread.

The OpenGL example is relevant to glSDL, too.

Yep, and it doesn’t let you access VRAM directly at all, so the whole
point is moot there - no possible shortcuts.

I know that’ll give OpenGL a hernia in Windows (contexts
are selected per-thread and can only be selected into one
thread);

I think that applies to most platforms to some extent, since the
idea is that professional 3D apps should be able to use multiple
contexts at once from different threads. (If you can’t do that,
you’ll have serious trouble keeping the GUI responsive.)

Direct3D allows accessing the same context from multiple threads,
which is very useful, since it lets me update streaming movie
textures in a thread, without having to synchronize (as long as I
arrange for the texture to not be in use at the time). That’s
impossible with Windows’s OpenGL implementation.

Not sure if it would work efficiently with any OpenGL implementation,
anyway. Depends on how fine grained the driver’s internal
synchronization is. We can at least assume that there is
synchronization somewhere, since it wouldn’t be possible to use more
than one context at a time otherwise.

Some OpenGL drivers have extensions for asynchronous texture
uploading, but I don’t know if there are any plans on including
something like that in the standard. As it is, the only reasonably
portable way of doing it is to update such textures piece by piece
and hope that the driver implements glTexSubImage2D() without
cheating. (Some drivers seem to keep a s/w buffer and upload the
whole texture for every call. heh)

//David Olofson - Programmer, Composer, Open Source Advocate

.- Audiality -----------------------------------------------.
| Free/Open Source audio engine for games and multimedia. |
| MIDI, modular synthesis, real time effects, scripting,… |
`-----------------------------------> http://audiality.org -’
http://olofson.nethttp://www.reologica.se —On Monday 31 May 2004 07.23, Glenn Maynard wrote:

On Mon, May 31, 2004 at 04:56:56AM +0200, David Olofson wrote:

Perhaps I’ve overlooked something, but why not start the loader in the
separate thread and draw the progress bar in a while loop in the main thread?
The loader would set the progress variable and a flag which tells the main
thread when to leave the loop.

I’m pondering these things as well at the moment, since our racing game takes
quite a while to set up the data for longer tracks. I’ll probably have to
implement a separate thread for Ogg music streaming as well though.

Marian.On Monday 31 May 2004 05:06, David Olofson wrote:

I think it should work as long as you’re using the SDL s/w blitters
and s/w surfaces only in the loader thread.

However, I think we’ve concluded that it’s not a good idea, unless you
actually have work to do while the loader thread is working on
individual files. It’s safer and much easier to just not bother with
that stuff.

I think it should work as long as you’re using the SDL s/w
blitters and s/w surfaces only in the loader thread.

However, I think we’ve concluded that it’s not a good idea,
unless you actually have work to do while the loader thread is
working on individual files. It’s safer and much easier to just
not bother with that stuff.

Perhaps I’ve overlooked something, but why not start the loader
in the separate thread and draw the progress bar in a while loop in
the main thread? The loader would set the progress variable and a
flag which tells the main thread when to leave the loop.

That was the idea, but that doesn’t avoid the problem, unless the
loader completely stays away from SDL surfaces. The problem is that
the SDL blitting calls aren’t thread safe, so weird things will
happen if you use them from multiple threads - even if you’re only
using SDL s/w blitters.

I’m pondering these things as well at the moment, since our racing
game takes quite a while to set up the data for longer tracks.

Well, if that work is just plain number crunching without SDL calls,
the approach you suggest will work just fine. If you need to use SDL
(for blitting between s/w surfaces and stuff), you’ll have to ensure
that only one thread at a time is inside one of those calls.

I’ll
probably have to implement a separate thread for Ogg music
streaming as well though.

That won’t be much of a problem, I think, as the decoding doesn’t have
anything to do with SDL directly. I’d suggest using a lock-free FIFO
or buffer ring between the decoder thread and the audio callback.

//David Olofson - Programmer, Composer, Open Source Advocate

.- Audiality -----------------------------------------------.
| Free/Open Source audio engine for games and multimedia. |
| MIDI, modular synthesis, real time effects, scripting,… |
`-----------------------------------> http://audiality.org -’
http://olofson.nethttp://www.reologica.se —On Monday 31 May 2004 17.32, Marian Schedenig wrote:

On Monday 31 May 2004 05:06, David Olofson wrote:

Yep, and it doesn’t let you access VRAM directly at all, so the whole
point is moot there - no possible shortcuts.

VRAM isn’t the issue; the issue is “can you update surfaces from another
thread”. OpenGL does this differently, but the issue is still relevant
(though it may not be to the actual glSDL implementation).

Some OpenGL drivers have extensions for asynchronous texture
uploading, but I don’t know if there are any plans on including
something like that in the standard. As it is, the only reasonably
portable way of doing it is to update such textures piece by piece
and hope that the driver implements glTexSubImage2D() without
cheating. (Some drivers seem to keep a s/w buffer and upload the
whole texture for every call. heh)

I wish GL had a list of software hints, eg.“GL_TEX_SUB_IMAGE_IS_INEFFICIENT”
(“glTexSubImage is not more efficient than glTexImage”), so software which
depends on things like that can fall back generically, instead of having
to special case cards.On Mon, May 31, 2004 at 03:57:05PM +0200, David Olofson wrote:


Glenn Maynard

Yep, and it doesn’t let you access VRAM directly at all, so the
whole point is moot there - no possible shortcuts.

VRAM isn’t the issue; the issue is “can you update surfaces from
another thread”.

Yeah, and you can update VRAM from any thread, provided you can map
VRAM into your address space - unless the driver explicitly maps the
memory so that only the calling thread can access it. (The only OS I
know of that may be capable of doing that is Linux.)

OpenGL does this differently, but the issue is
still relevant (though it may not be to the actual glSDL
implementation).

Well, OpenGL can’t do what I’m talking about - mapping VRAM to the
application - at all, so OpenGL (and thus glSDL) simply require that
you play by the API rules.

That said, you could sort of fake it with glSDL, just like with any
other SDL backend:
1) Have the main thread lock the surface.
2) Let the loader thread mess with the locked memory.
3) Have the main thread unlock the surface.

Note that in the case of glSDL, “locked memory” would be system RAM
(which can be accessed from any thread), and unlocking the surface
results in the surface being uploaded as one or more OpenGL textures.

I wish GL had a list of software hints,
eg.“GL_TEX_SUB_IMAGE_IS_INEFFICIENT” (“glTexSubImage is not more
efficient than glTexImage”), so software which depends on things
like that can fall back generically, instead of having to special
case cards.

Yeah, that would be handy…

Of course, you could benchmark the system as part of the installation
or something, to figure most of that stuff out. I’ve seen quite a few
games do that to generate a “recommended configuration”, and that
seems to work rather well.

I was thinking about hacking a lib that does something like that with
SDL. (That’s probably been discussed on this list on several
occasions as well.) You’d hand it a number of video modes to try, and
it would return the blit bandwidth for opaque, colorkeyed and alpha
blended surfaces, per-rectangle cost and stuff like that.

The problem is that the equation needed to translate such low level
info into a preferred rendering configuration tends to be complex
enough that it’s easier and more reliable to just do an automatic
benchmark of the actual graphics engine of the game instead.

//David Olofson - Programmer, Composer, Open Source Advocate

.- Audiality -----------------------------------------------.
| Free/Open Source audio engine for games and multimedia. |
| MIDI, modular synthesis, real time effects, scripting,… |
`-----------------------------------> http://audiality.org -’
http://olofson.nethttp://www.reologica.se —On Monday 31 May 2004 21.20, Glenn Maynard wrote:

On Mon, May 31, 2004 at 03:57:05PM +0200, David Olofson wrote:

David Olofson wrote:

I’ll
probably have to implement a separate thread for Ogg music
streaming as well though.

That won’t be much of a problem, I think, as the decoding doesn’t have
anything to do with SDL directly. I’d suggest using a lock-free FIFO
or buffer ring between the decoder thread and the audio callback.

Here’s my 2 cents worth of music streaming with SDL :

I have done mp3 music streaming for a game. Contrary to all sane advice,
I do decoding directly inside the callback function :slight_smile:
I use an optimized mmx mp3 decoder (mpglib, to which I backported mmx
optimizations from mpg123) and a 4096 samples buffer (this is with 44Khz
16 bit stereo sound, I found 4096 was the minimum on windows to avoid
sound skips with that quality, whatever I do). I know that’s quite
unreasonable but it happens to works fine on windows and linux.

The trick in all this is that the mp3 decoding time is not that big if
you compare it to the time it takes to do a memory copy in the threaded
decoding approach. In fact, a big part of the computation time is spent
waiting for cache misses in both cases. It’ll probably work with ogg
too, although I couldn’t find any ogg decoder that I could drop in in my
code that was as easy to integrate as mpglib (I wanted to integrate it
in my code to be able to add in my optimizations).

Stephane

The trick in all this is that the mp3 decoding time is not that big if

MP3 decoding time isn’t the issue. After all, you have to spend the CPU
decoding it at some point; you don’t magically get more CPU by moving
something to a thread (ignoring SMP).

The problem is that if you have a small sound buffer (say, 10ms), disk
access is not consistent enough: you can’t count on the kernel always reading
ahead. Eventually, you’re going to need data off the disk that isn’t in
cache, and the drive is going to have to seek, which is usually going to take
10ms or more, which will stall the audio pipeline and underrun.

16 bit stereo sound, I found 4096 was the minimum on windows to avoid
sound skips with that quality, whatever I do). I know that’s quite

4096 is a massive buffer; at 44.1khz, that’s 90ms, which is large enough
to hide the above problem. DirectSound has bugs that prevent it from
working with small buffer sizes; I can only go down to 2048. I can easily
use a 512-frame buffer (11ms) with ALSA; at that size, you can’t be doing
any disk reads in the mixing thread.On Tue, Jun 01, 2004 at 12:17:54AM +0200, Stephane Marchesin wrote:


Glenn Maynard

Glenn Maynard wrote:

The trick in all this is that the mp3 decoding time is not that big if

MP3 decoding time isn’t the issue. After all, you have to spend the CPU
decoding it at some point; you don’t magically get more CPU by moving
something to a thread (ignoring SMP).

The problem is that if you have a small sound buffer (say, 10ms), disk
access is not consistent enough: you can’t count on the kernel always reading
ahead. Eventually, you’re going to need data off the disk that isn’t in
cache, and the drive is going to have to seek, which is usually going to take
10ms or more, which will stall the audio pipeline and underrun.

Well, until now, the OS was smart enough to ensure that I get no
glitches. But you’re right, that’s why I’m also seriously considering
loading the whole mp3 at once at the beggining. After all an mp3 is only
3Mb or so… Well, that is, provided I can get around that other issue I
have under windows (see below).

16 bit stereo sound, I found 4096 was the minimum on windows to avoid
sound skips with that quality, whatever I do). I know that’s quite

4096 is a massive buffer; at 44.1khz, that’s 90ms, which is large enough
to hide the above problem. DirectSound has bugs that prevent it from
working with small buffer sizes; I can only go down to 2048. I can easily
use a 512-frame buffer (11ms) with ALSA; at that size, you can’t be doing
any disk reads in the mixing thread.

I know, but I can’t get around it for some reason. And that’s not
related to decoding/reading mp3 inside the callback thread, I also tried
with a simple sine-wave generator. Whatever I do, I have glitches when
under 2048 (even at 2048 there is an occasionnal tick, that’s why I
chose 4096). The system I tried this on isn’t the fastest but should be
able to handle this : celeron 433 with an isa AWE32 (it’s doing OpenGL
at the same time, but still). Maybe the isa bus is at fault… Anyone
who knows how to improve the situation under windows is welcome to share
his ideas :slight_smile:

And well, 100ms is bearable in a game situation if you ask me (games
usually don’t rely too much on sound feedback). What I don’t like
though, is when playing a midi keyboard linked to a computer, where you
rely solely on what you hear for feedback. A friend of mine has such a
setup with one of these very low latency (<10ms) sound cards, and you
can “feel” it when you increase/lower the latency. I’ve never had this
feeling in a video game.

Stephane>On Tue, Jun 01, 2004 at 12:17:54AM +0200, Stephane Marchesin wrote:

4096 is a massive buffer; at 44.1khz, that’s 90ms, which is large enough
to hide the above problem. DirectSound has bugs that prevent it from
working with small buffer sizes; I can only go down to 2048. I can easily
use a 512-frame buffer (11ms) with ALSA; at that size, you can’t be doing
any disk reads in the mixing thread.

I know, but I can’t get around it for some reason. And that’s not
related to decoding/reading mp3 inside the callback thread, I also tried
with a simple sine-wave generator. Whatever I do, I have glitches when
under 2048 (even at 2048 there is an occasionnal tick, that’s why I

DirectSound is buggy, and usually can’t handle buffer sizes smaller than
~40ms. I think this is documented somewhere in MSDN, though they don’t
admit that it’s a bug. :slight_smile:

And well, 100ms is bearable in a game situation if you ask me (games
usually don’t rely too much on sound feedback). What I don’t like
though, is when playing a midi keyboard linked to a computer, where you
rely solely on what you hear for feedback. A friend of mine has such a
setup with one of these very low latency (<10ms) sound cards, and you
can “feel” it when you increase/lower the latency. I’ve never had this
feeling in a video game.

I notice it in FPSs: footsteps playing well after you stop moving, gunshots
not in sync with muzzle flashes, etc.On Tue, Jun 01, 2004 at 01:25:30AM +0200, Stephane Marchesin wrote:


Glenn Maynard