View Single Post
Old 11-09-02, 10:52 PM   #6
Registered User
Join Date: Sep 2002
Posts: 2,262

OK. First, let me say that Windows does a lot of crap behind users' backs. I wouldn't be surprised if nVidia cards didn't actually interrupt anything when a monitor vblank happened. It wouldn't surprise me if Windows actually kept track of that, and made known a way (possibly not even a true, exported Windows API call, but something that's guaranteed to be not changed, and made known to only driver developers) to set up a callback to track it. A lot like an interrupt, but entirely on the software side. Which would make writing that way of doing it into the drivers infinitely easier.

Saying that windows drivers can do something that Linux drivers can't doesn't necessarily mean that it's a bug in anything, that's all I'm saying. Go ahead and ask the nVidia people to fix it, but don't be surprised if it isn't possible. Don't be surprised if it is possible, but never happens. My opinion of all this is that you are one program among at least thousands. If it's really hard to do, but you're the only person asking for it, I wouldn't be surprised if it doesn't get done. Even worse, if adding that feature must break (for some arcane reason that none of use can see, because the drivers are still closed-source) the other 99.9% of programs out there, then I really wouldn't be surprised to never see it happen. Not that I'm saying this is the case (again, I don't know any more about the hardware or the closed-source part of the driver than you do), but it is a possibility, however remote.

Since you're writing a program that acts one heck of a lot like a window manager, why not just look at what other people have already done with window managers that look like Aqua? There are quite a few Aqua look-alike skins for normal window managers... and if you want it to act like Aqua too, why do you NEED OpenGL? Why not just use the X RENDER extension, which accelerates 2D graphics in X11? I wouldn't think it'd be too hard to do most of what I've seen Aqua do with just 2D graphics anyway... but hey, I'll admit that I haven't seen it do much at all.

With regard to the poll, it does not return < 0 on my test program. It simply returns immediately, without waiting.
OK, very well. But I was seeing at least two different things that could have been wrong with the program, that could have caused it to return an error, and no indication whatsoever that you had ever checked for any of those errors. I assume that you have tried changing the order of open() and the setop of the poll() struct's fd member around, with no change? I also assume you've tried reading the man page for poll, and tried changing the event member of the struct to be at least legal (here's a hint that should be blindingly obvious from that man page: the legal value for what you appear to be trying to do is 7, for POLLIN, POLLPRI, and POLLOUT all set), with no change? And you aren't saying how you determine the function to be coming back immediately -- how are you doing that?

Once you at least make this poll() stuff valid C syscall requests that still don't do what the guy said they used to, then OK, look into asking for the feature (and yes, "asking for a feature" is all that you are doing, you are not reporting a bug, you are not reporting a "broken implementation", nothing anything as melodromatic as all that -- a difference between the Windows and Linux drivers is not anything like a difference between the documented Linux behavior and the actual Linux behavior). But frankly, I want to see that you've tried making it work yourself first. I see that you've tried glXSwapBuffers(), OK, very well. But bringing up this hack, and then having it be completely wrong from at least two different angles, and then saying that it doesn't work, just makes perfect sense -- by all rights that code shouldn't have ever worked in the first place! See if fixing it helps.

Just as one more little thing:

If I hadnt have reniced glxgears, the FPS would have shot down to about 20-30FPS.
As it should have! You are running two different processes that are both trying to take all of the CPU -- one supposedly legitimately, one supposedly not -- both at the same priority. Is it any wonder that they split the CPU evenly? Is it any wonder that of the 1/60 of the time that the vblank is actually occurring, you miss half of it? Which drops your framerate down to 30fps, or even 20? See, when you're syncing to vblank, the framerate must be a divisor of your refresh rate -- either 60fps, 30fps, 20fps, 15fps, 12fps, 10fps, 8.57142fps, 7.5fps, etc., or etc., on down. Basically, the framerate must be, at all times, 60fps divided by some integer. If you just miss one vertical refresh, you will stall until the next, which drops the framerate down one more step. Assuming the unthinkable (or at least the very, very unikely) happens, and you miss 60 times in a row, you'll get 1fps for that frame.


Then, last of all before I just hit submit and say screw it, I've been typing and editing for waaaay too long, a couple of things I'd think about trying to do in your program:

Do the waiting yourself. You're writing a window manager for X, basically, so you should have access to X's refresh rate. So then make it a habit on every frame to call some usleep() or whatever function for the majority of the refresh rate time, then call glXSwapBuffers() with vsync enabled.

Or, use something like BogoMIPS in your program. Fork your execution stream (but don't use fork() itself, just clone() or pthread_create() or something like that), then do all your GL rendering in one stream, and set a global (but mutex'ed) variable at the end. In the other stream/thread, just keep mutex'ed checking the global variable, and when it's set, join() with the other thread. But in the meantime, be incrementing a counter. Once you join, figure out how much of the vblank time you haven't used (from that counter), and then usleep for a short time less than that. Then call glXSwapBuffers.

Something like this pseudocode:

// initialization:


do {
} until(exactly one second has passed since counter got set to zero);

maxcounter /= get_refresh_rate();

// in your rendering loop:


if(I'm the child thread) {
    do all rendering;

        global_var = 1;
else {
        local = global_var;

    if(local != 1) {
    else {
        usleep(a number of microseconds a few less than a number that,
               had I been counter++'ing during that amount of time, would
               have made counter be equal to maxcounter);

        join(parent_thread);  // just to clean up after it; it's already done

        glXSwapBuffers(whatever is needed here);
Registered Linux User #219692

Last edited by bwkaz; 11-09-02 at 10:58 PM.
bwkaz is offline