[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Linux sound (and threads)



Jeff Read wrote:

> > This is very curious, because I also thought of that approach. That's
> > exactly the one used in Doom, and it does lag, but the reason it lags
> > might be as simple as not flushing the pipe after a write (easily
> > fixable) to the hard ones like the context switching required.
> 
> I have not noticed any lag while playing Gnudius. Playing Quake 2 under
> Windows lags a lot more sound-wise; I think this has to do with thunking
> DirectSound routines down to the standard Windows multimedia layer, so
> that I can just *get* sound on my ancient sound card :)

Yes, that what happens when you do not have DirectSound drivers... :-)

If I remember correctly, in Doom you could invoke the lag by going at a
place where there was enough monsters to push the machine a bit (it
didn't need a lot, just enough monsters that the machine is not on an
empty load), and fire off the shotgun. You'd see that the shotgun
already had started to raise a bit when the sound effect actually
started. The pain sound effects from the monsters were also prone to lag
if you had enough of them. But that's a long time ago.

> > What I actually find curious is that when I considered this approach, it
> > turned out that the trouble of crossing the process boundary (this
> > involves managing resources in both processes, pipes, assigning IDs to
> > the sound effects and keeping track of them) was *more* trouble than
> > what was saved by having the sound server "work by itself". Calling the
> 
> You make it sound like a lot more trouble than it really ended up being
> (at least for me). I coded xhsound in the space of a week or so and it
> worked reasonably. A few tweaks made the lag go away for good :)

I guess I'm lazier. :-)

(our sound module took a short evening to hack by two persons)

> > process_sound() method is *really* easy to do, considering that games
> > normally have a few hot spots where they go really often, like frame
> > buffer page flipping or input polling.
> 
> I would have to agree with you that this is probably the better method.
> But, I'm lazy, xhsound works fine and I haven't Gotten Around To Fixing
> It. :)

Well, if it works correctly now, it should be fine, but I think that in
the long run, out of process could be a bad idea, particularly in the
area of games like Quake2, where gamers complain that a single machine
gets 34 fps in Windows and only 33.2 in Linux... ;-)

> > Also, count in additionnal time used to manage locks and mutexes for
> > concurrent access, this just goes thru the roof for close-to-the-metal
> > game programming.
> 
> Back when I was hacking Win32 the number of threads I had maxed out at
> 2, one that repeatedly positioned the objects and redrew the screen; and
> another which handled input events. This was *relatively* easy to manage
> and allowed me to take a programmer's shortcut by putting the
> move/render thread in an infinite loop and the event thread in a
> busy-wait. These days I just use the single thread and put everything in
> the same big loop.

With only two threads in such a manner, you could only use a mutex for
an queue with move events coming from the input thread, so that's not
too bad as far as "complicated" goes. But is the render thread looking
often at the queue? If so, it has the lock often, and the input thread
has to wait. Or if the input thread got the lock, the render thread goes
dead in the mean time... This is taking it a bit simply though. I guess
you could "try" to get the lock for a few milliseconds, then, seeing
that you can't have it now, go and render some stuff. But now it is
getting more complicated...

> > In Quadra, we use matrix-based explicit dispatching between small units
> > of execution, a kind of dynamic state machine, which is deadly
> > efficient, switching "threads" at moments we precisely defined. Nothing
> > is concurrent, so no locks/mutexes are needed.
> 
> I dunno what that means but it sounds a lot like what Sprite32 does
> which is to call a user-defined method for each sprite before doing a
> screen refresh. Using C++ inheritance you can make each game object into
> an independent finite-state automaton which reacts to certain events
> such as collision with another sprite or passing by a certain X
> coordinate on the screen. This method is amenable to both single- and
> multi-threaded execution and frees me from having to think about the
> main game loop :)

What this means is that you have a scheduler object with a bunch of
"thread" objects. The thread objects have a virtual method called
"step", which does a "bit" of work. This is the "small unit of
execution". The explicit dispatching is that each "thread" decides of
when to do the context switch themselves, when their step() method
return. Then, the scheduler object calls the next "thread" object step()
method, and it goes around that way. The threads have a "die" property
that they can set before returning from their step() method to tell the
schedule they are to be removed from the list.

The trouble with this method is that to get a "multiple thread running
at once" effect, you have to keep the processing in the step() method at
a minimum. For a small thing, like a fade thread, this is simple, you
just rotate the color map then return, but for anything more
complicated, you have to cut the task in small parts, keep context
between step() calls and so on. This:

void task() {
	int i;

	for(i = 0; i < 10; i++) {
		printf("%i\n", i);
	}
}

Becomes:

void step() {
	/* the i counter is now a property of the thread object */

	if(i < 10) {
		i++;
		printf("%i\n", i);
	} else {
		die = true;
	}
}

And that was a simple example. Now go and apply this to the recursive
block falling algorithm of Quadra!

> (See? C++ object orientation is good for something.... :))

Yeah, I agree, except with the C++ part of the sentence... ;-) (C++
freaks me out, I prefer Objective-C, as weird as I could be)

> > The problem is, this is a pain to program to. We found that cool library
> > that could do non-preemtive threads in Unix at
> > http://www.engelschall.com/sw/nps/, but it doesn't work in Windows (uses
> > a wizard-level hack involving signals and manipulating stack frames). A
> > similar library that would work on both Unix (at least Linux) and
> > Windows would be extremely cool. With a XFree86-style license or the
> > MozPL (not the GPL, as we are into *commercial* games, and even the LGPL
> > is a pain in the ass, it prevents statically linked binaries, for
> > "dubious" systems (but is mostly fine otherwise)).
> 
> Guile (a GNU embedded interpreter) comes with a non-preemptive thread
> library called QuickThreads (or QT, not to be confused with the Qt
> widget set of KDE fame). I dunno how well it works for your purposes, or
> how it works in Windows.

Hmm... QuickThreads, eh? Sure that I'll take a look at this! But I
wouldn't wager this library being under a looser license than the
LGPL...

-- 
Pierre Phaneuf
Ludus Design, http://ludusdesign.com/
"First they ignore you. Then they laugh at you.
Then they fight you. Then you win." -- Gandhi