3
\$\begingroup\$

I have a Direct3D application which uses a PRESENTATION_INTERVAL_ONE implementation. Pity, its blocking and eating my CPU waiting for Present. I ended up searching for raster scan (BitBlt) solutions, sample code from the Internet, but none of them actually worked well - failing to do the exact timing prior to Present call.

Question

So, ideal solution for me would be using PRESENTATION_INTERVAL_ONE (or if its not possible - IMMEDIATE), calling a function

// blabla code
waitForVsync(); //loop here with sleeps or whatever, freeing my CPU
()->Present();  //just in time, no blocking wait, yay!

And upon a return I call Present, which is, lets say, 1ms for _ONE and 0ms for _IMMEDIATE prior to physical top scanline.

I know I am asking a lot and do not hope for a solution in 2011 year, as Microsoft also has failed to improve in this area.

To clarify the issue, I provide a link with a more detailed analysis of a problem: http://www.virtualdub.org/blog/pivot/entry.php?id=157

\$\endgroup\$
5
  • \$\begingroup\$ What about it? That's the basic (busy) blocking function, while(true){/* ... */}. \$\endgroup\$
    – Blindy
    Jul 9, 2011 at 4:16
  • \$\begingroup\$ Yes. But giving up your time slice to other threads kills your determinism (ie Present won't be called as soon as VSync happens, it will be called sometime in the future when you get CPU time again). You actually want busy waiting. Regardless, both types of functions are blocking. Nonblocking functions involve threads (or Windows messages) and return instantly, before their condition is true. \$\endgroup\$
    – Blindy
    Jul 9, 2011 at 4:20
  • \$\begingroup\$ Which version of DirectX are you using? 9, 10 or 11? \$\endgroup\$ Jul 9, 2011 at 17:39
  • \$\begingroup\$ I am using Version 9 \$\endgroup\$
    – user757808
    Jul 9, 2011 at 20:51
  • \$\begingroup\$ Even if this isn't game-dev specific, it's certainly game-dev relevant, and you're much more likely to get a useful answer here. Also re-tagged as PC as on 360 you most likely don't care about the blocking wait. \$\endgroup\$
    – MrCranky
    Jul 10, 2011 at 11:37

3 Answers 3

1
\$\begingroup\$

It seems to me that you're trying to achieve a graceful co-existence with other processes on a PC game? I.e. to not occupy the entirety of the CPU all the time, so as to allow other processes to run (and if you're running on a laptop, to allow the speed-stepping to power down the CPU and preserve battery life)?

I've certainly tried to achieve this in the past, and never fully settled on a solution I liked. I never tried Crowley9's method of using the DONOT_WAIT flag, but to me that smells like closer to the right solution. If Present only blocks when the present can't be queued (because there's already another present queued), then you don't need to call Present 'just before' the VSync happens, you just need to sleep until it's likely that another call to Present will succeed (which will be any time after the next VSync).

The root problem is that you don't really know when the VSync occurs, and a system like that needs to stay synchronised.

The basic gist of my solution was: don't use Present to find out where the VSync boundary is, do the timing yourself, and make sure you're calling Present as close to the appropriate time as possible. If you're late, it won't matter because of the internal buffering D3D does, and if you're early then it will simply block and the worst you'll incur is some extra CPU usage.

What I did do was to use (microsecond accurate) timers, track the elapsed frametime, and sleep accordingly. There's no magic flag or function I know of to say 'wake me when VSync's about to happen', because there isn't an event available that is signalled when a VSync is about to happen. So instead I looked at how long the frame had taken so far, figure out when I thought the next VSync would occur, subtracted a grace period (I think I left 3ms), and call ::Sleep.

E.g. if the frame processing took 2ms, I would get to the Present call early. If I called it straight away, I'd expect it to block (at 100% CPU) for 14ms. Instead, I call ::Sleep(11), and then the subsequent Present only blocks for ~3ms). If the frame processing took longer than 13ms, I'd just call Present without sleeping. If it took longer than 16ms, I'd switch down to 30Hz rendering (with some hysteresis), and adjust the Sleep timings accordingly.

BUT I was never particularly happy with this solution, for several reasons. If you're running on a loaded system, Sleep is by no means guaranteed to give you control back in that 3ms window, you have no good control over the OS time-slicing.

Worse, the minimum sleep time is supposedly 10ms, anything less than that and it ends up sleeping for 10ms regardless of what you specify (in practice it's way more variable than that). So you also have to track how long you actually slept for, and factor that into your logic (so you avoid sleeping if you're running late). And then you end up calling Present late one frame, then avoiding the sleep the next frame and calling it early the next. E.g.

0ms (0ms) : start
10ms (10ms) : process frame 1
10ms (20ms) : try to sleep for 3ms, end up sleeping 10ms
0ms (20ms) : present late, succeeds straight away
10ms (30ms) : process frame 2
2ms (32ms) : don't try to sleep, present early, blocks till the next vsync

So for managing 60Hz rendering, the granularity of the time-slicing / Sleep function makes this a not great solution. And it also doesn't take into account the actual refresh rate of the system!

\$\endgroup\$
2
  • \$\begingroup\$ Did you consider using GetRasterStatus? I even saw someone calculating time deltas, but was unable to put it to use in my app. BTW, its not a game, just a real time video rendering engine from the ccd camera. I got tearings, thats why I seek vsync \$\endgroup\$
    – user757808
    Jul 10, 2011 at 10:23
  • \$\begingroup\$ I remember seeing the function, but think I discarded it as not solving the core problem: 'is the Present call going to block or not?' Tearing should only happen with INTERVAL_IMMEDIATE, which you clearly don't want; and Present will block if you can render frames faster than they can be presented (e.g. with INTERVAL_DEFAULT or INTERVAL_ONE). You need to trust Present/INTERVAL_ONE to do its job, and sort out the VSync for you. What you'd like to do is to try to call Present just a little more than it needs to (like, after 95% of the refresh interval), but sleep whenever you can. \$\endgroup\$
    – MrCranky
    Jul 10, 2011 at 11:35
2
\$\begingroup\$

To address the specific issue of waitig on present: Present does not work quite the way you think: it does not block and wait for VSync. CPUs have not been synchronous with GPUs in many years. Rather, it queues up a wait-for-a-vsync-and-flip operation, and returns. The GPU will process it when it gets to it.

If you are seeing present take a non-trivial amount of time, that is because it is waiting for an older frame to finish (there is a limit on the number of frames in flight). If you want to do other processing, rather than waiting, you can use the DO_NOT_WAIT flag to do a non-blocking present that will return whether or not the present was queued successfuy, or if the hardware was busy.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ DONOT_WAIT works only with additional swapchains, and I am using the default one. Tried that, doesnt affect using default swapchain. PRESENT_INTERVAL_ONE vs PRESENT_INTERVAL_IMEDIATE is about 15% difference in CPU load, the latter, also, produces tearing \$\endgroup\$
    – user757808
    Jul 9, 2011 at 20:53
1
\$\begingroup\$

As this has moved to gamedev I'll assume it's game related, despite nothing that alludes to gaming in the question.

The usual answer to letting the CPU carry on and do work once you hit a 'Present' is to double/triple buffer the backbuffer, which allows the CPU/GPU to start work on the next frame before the one you've just finished rendering has been displayed (if you didn't double-buffer then you'd be rendering the next frame over the one you'd just finished and would be a mess). The flip then is queued up behind the scenes and the CPU can carry on doing other work. If you don't do much in a frame it's possible you could hit 'Present' again before either of the backbuffers have been displayed and it could block again.

Is the question above actually about 'VirtualDub' (as the info there seems to be from 2007), or is it related to making games? I'm still not sure exactly what it is you're trying to achieve or what you want to free the CPU up for prior to the Present occurring.

\$\endgroup\$
3
  • \$\begingroup\$ If it will help - I am using SwapchainMethod Copy, with BackBuffer count 1 \$\endgroup\$
    – user757808
    Jul 9, 2011 at 20:55
  • \$\begingroup\$ The question seems to be about not 'how do I do more work while waiting for VSync', but rather 'how do I gracefully let other processes do stuff', which to me is a laudable aim. \$\endgroup\$
    – MrCranky
    Jul 10, 2011 at 9:32
  • \$\begingroup\$ @MrCranky Yes indeed very laudable, but as we see on this page, largely a fool's errand. Because nailing a clockwork VSync boils down to determinism, as Blindy so correctly noted, specifically the type of timing guarantees that RTOSs exist to provide. As you know, general-computing (i.e. non real-time) OSs like Windows or Linux are provably fallible here, by design, and can only hope for approximate or empirically grudging results. My point is, you shouldn't be unhappy with your excellent solution above; after eons of research I've basically arrived in exactly the same place. \$\endgroup\$ Feb 22, 2017 at 12:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .