#low@irc.freenode.net logs for 13 Sep 2005 (GMT)

Archive Today Yesterday Tomorrow
LoW homepage

[02:55:13] --> Coren_ has joined #low
[02:55:13] --- ChanServ gives channel operator status to Coren_
[02:56:29] <Coren_> Bah. So SDL is *mostly* nice, but it has got one major piece of braindeath: SDL_WaitEvent doesn't actually wait for events. it's a just a busy loop with 10ms delay. Bah.
[02:59:22] <Coren_> Which explains the odd timing behavior I've been having.
[02:59:24] * Coren_ grumbles.
[03:05:04] <servus> Ah
[03:05:22] <servus> I poll, and have good resutls
[03:06:05] <servus> SDL_Event ; while( stillrunning ) { while( SDL_PollEvent( &Event ) ){ /* Process Event */ } }
[03:06:17] <servus> SDL_Event ; while( stillrunning ) { while( SDL_PollEvent( &Event ) ){ /* Process Event */ } SDL_Delay( 0 ); }
[03:06:40] <servus> Maybe render near the SDL_Delay. Works well for me.
[03:07:23] <servus> SDL_PollEvent is not a blocking call. It just pops an event off the event stack and returns it, or nop's on an empty event stack and returns NULL immediately.
[03:38:54] <Coren_> Polling is Evil(tm). I'm already busy doing a blocking version of SDL_WaitEvent.
[03:41:07] <Coren_> Otherwise, if I have the SDL event loop in a thread, it steals the CPU away from the other threads to spin. Ugly.
[03:43:10] <Coren_> int SDL_WaitEvent (SDL_Event *event)
[03:43:11] <Coren_> {
[03:43:11] <Coren_> while ( 1 ) {
[03:43:11] <Coren_> SDL_PumpEvents();
[03:43:11] <Coren_> switch(SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_ALLEVENTS)) {
[03:43:11] <Coren_> case -1: return 0;
[03:43:13] <Coren_> case 1: return 1;
[03:43:15] <Coren_> case 0: SDL_Delay(10);
[03:43:17] <Coren_> }
[03:43:19] <Coren_> }
[03:43:21] <Coren_> }
[03:43:23] * Coren_ gags.
[03:45:28] <Coren_> And, you know, the funny part is that events are actually generated in a separate thread that *does* sleep. Condition variable, anyone?
[03:47:51] <servus> It's not classic polling.
[03:48:29] <servus> Well it is, but doesn't have to be used like that :)
[03:48:37] <Coren_> Well, SDL_PeepEvents() just locks the event queue mutex, checks if there is one (and returns it) then unlocks the mutex and return.
[03:49:11] <Coren_> Hm. I note that the SDL thread stuff implements mutexes, but not condition variables. Win32 limit?
[03:49:18] <servus> SDL_PollEvent( SDL_Event* ) will nop immediately and return NULL on no event.
[03:50:15] <Coren_> I know, SDL_PollEvent() is just a tiny wrapper around SDL_PeepEvent(). But then it means /your/ event loop will be busy doing the spinning instead of SDL. Same difference. :-)
[03:51:28] <Coren_> Ack! The event queue is fixed-sized *and* the event code will just drop events on the floor if it fills.
[03:52:27] <servus> How big is the default?
[03:52:53] <servus> You can guarantee polling framerate, though.
[03:53:09] <servus> If you don't put polling and rendering in the same threads, etc.
[03:53:19] <Coren_> 128. Should be reasonable for most cases I guess; it's the principle that sucks. One should simply block when the queue is full to give a chance for the thread draining the queue to do its work.
[03:54:10] <Coren_> Bah. Open Source. I am not allowed to whine unless I'm willing to fix and contribute back. I am. *churn churn churn*.
[03:54:14] <servus> How do you tell the OS to block? :)
[03:55:08] <Coren_> You don't, but however you're getting the events you can just stop while you wait for the queue to drain. Every event would be coming off a file descriptor anyways (or, I guess, message queues under windows)
[03:55:39] <servus> Well, then you're passing the responsibility of storing all events to the OS, which at least makes you not responsible.
[03:55:53] <Coren_> Know of any reason why condition variables wouldn't work under Win32? They implement posix threads, right?\
[03:56:02] <servus> Maybe Windows (or other OS) does not allow for rejection of an event?
[03:56:17] <servus> Dunno.
[03:57:35] <Coren_> for(;;) { read_one_thing_from_os(); stuff_thing_in_queue(); } works fine even if stuff_thing_in_queue() blocks; That can cause event lag, but that's arguably better than just dropping an event on the floor. Think 'key release' missed. :-)
[03:58:18] <Coren_> And if you have a blocking queue, you get a blocking queue drain for (basically) free-- and thus an SDL_WaitEvent() that releases the CPU when nothing is happening.
[03:59:54] <servus> What's wrong with a tiny amount of spinning on the part of your program, if you are trying to maximize CPU usage anyway?
[04:00:59] <Coren_> Well, for one, you're not always trying to maximize it. In order to emulate UW2 correctly, the world state updates only 25 times per second-- so unless you're moving your camera around there /is/ nothing to do between those ticks.
[04:02:19] <Coren_> And maximizing CPU use isn't bad per se-- as long as you're actually using it. My rendering loop lives in its own thread; the way things are now the event draining thread steals the CPU to just loop waiting for event rather than release it for the important stuff.
[04:02:30] <servus> Well, SDL_Delay frees up the processor, and doesn't merely spinlock for an amount of time... So you could sleep your thread for all the time you're using it. It's a bit more work on the programmer's part, but not terrible.
[04:03:01] <servus> s/using it/not using it/
[04:03:21] <Coren_> The problem with SDL_Delay is that it release the processor for _too_ long; it's waiting for a (minimal) time period rather than "until something happens".
[04:04:36] <Coren_> It's just better programming practice anyways. Polling was already a bad idea in the 70's. It's just that DOS caused a lot of programmers to get bad habits. (You don't get used to releasing the CPU if you have nothing to release it to/for)
[04:05:17] <servus> SDL has no event callback functionality?
[04:05:24] <Coren_> Well, and there are RT applications where you need very short and very fine delays that are best done with a spinlock, but that's the exception not the rule.
[04:06:21] <Coren_> No, it doesn't. And the odd part is: it's got everything it could need to implement it. A properly reentrant even queue filled from an intependent thread, all the correct locking bits, everything.
[04:06:43] * Coren_ shrugs.
[04:07:03] <Coren_> Well, rather than whine about it, I'll just implement it and submit it. :-)
[04:07:27] <servus> Have fun testing it on 6 OS's :P
[04:08:53] <Coren_> Not much need, it seems, I can implement without going to the machine dependent bits: The've got condition variables in there. Why they aren't using 'em...
[04:09:01] <Coren_> So it turns out this'll be a simple patch.
[04:10:27] * Coren_ laughs out loud.
[04:10:38] <Coren_> Judging by the man pages, this is *very* new. :-)
[04:10:54] <Coren_> DESCRIPTION
[04:10:54] <Coren_> Creates a condition variable.
[04:11:05] <Coren_> That's about the extent of the man page. ;-)
[04:11:56] <Coren_> Might explain why they aren't using 'em.
[04:12:00] * Coren_ shrugs.
[04:13:19] * servus looks for callback functionality he seems to remember being alluded to...
[04:13:45] <Coren_> I'm in the middle of the event code right now. No such beast. At least not in 1.2.9
[04:14:53] <servus> http://www.libsdl.org/intro/whatcanitdo.html "Events are passed through a user-specified filter function before being posted to the internal event queue."
[04:16:15] <Coren_> Hm. You'd mean doing something like discarding everything from the filter and handling in the filter function? How... cruddy! :-)
[04:16:33] <Coren_> And that forces you to reimplement a queue.
[04:16:51] * Coren_ checks the source.
[04:17:24] <servus> It says user-specified filter function, not a user-overwritable filter.
[04:17:24] <Coren_> And that'd be a bad idea anyways: the filter function is called from the event generating thread.
[04:17:43] <servus> As if it's an optional function that is called for every event, if the function is registered.
[04:17:46] * servus shrugs
[04:18:15] <Coren_> All it does is pass events to a user-specified function just before it gets on the queue. The function returns a flag (put in queue/discard).
[04:18:39] <Coren_> So you _could_ handle the even in the filter function, and just always return the flag to discard.
[04:19:21] <Coren_> But then you'd be doing the handling in the thread that collects events from the OS.
[04:20:39] <servus> Well, all of my direct event handling is very lightweight... Just calls to set up variables and do minor calculations.
[04:20:42] <Coren_> Bah. There's a simple, clean solution that requires only a single condition variable and about 12 lines of code.
[04:39:32] <Coren_> Voila. works perfectly.
[04:41:15] <Coren_> And the whole diff is 61 lines. Not like it was painful. :-)
[04:41:23] <Coren_> (including context)
[04:45:31] <servus> So you specify a callback for handling events now, and it is handled in the main program thread?
[04:49:15] <Coren_> Oh, no, I just have SDL_WaitEvent() block when there are no events.
[04:50:01] <Coren_> My framerate just went from 120 to 150 or so with that simple fix since now the rendering thread gets all the CPU when there are no SDL events. :-)
[04:50:35] <Coren_> (All of this depends on SDL_INIT_EVENTTHREAD being specified when you SDL_Init(), obviously) :-)
[04:51:47] <Coren_> But if you want callbacks, you can now just for(;;) { SDL_WaitEvent(&e); my_callback(&e); } or whatever. No CPU cost unless there are events.
[04:52:09] <Coren_> (Which is, basically, exactly what I'm doing)
[04:52:38] * Coren_ goes to sleep.
[04:53:22] <servus> Well you could just comment out SDL_Delay :)
[04:53:24] <servus> Night.
[11:58:04] <-- exultbot has left IRC (signing off...)
[12:06:57] --> exultbot has joined #low
[12:06:58] --- Topic for #low is: Labyrinth of Worlds: Ultima Underworld II revisited (http://low.sourceforge.net)
[12:06:58] --- Topic for #low set by Coren_ at Tue Aug 16 03:03:38 2005
[15:27:39] <-- Coren_ has left IRC (Remote closed the connection)
[18:13:56] --> Khelz has joined #low
[18:53:36] <-- Khelz has left IRC ()
[21:01:02] --> Khelz has joined #low
[21:14:34] <-- Khelz has left IRC (Read error: 54 (Connection reset by peer))
[21:19:22] --> Khelz has joined #low
[23:00:37] <-- Khelz has left IRC (Read error: 104 (Connection reset by peer))