1
\$\begingroup\$

I'm working on a few game dev tools which use OpenGL or DirectX to render 3D models (viewing).

Consider your favorite 3D modeling software package (Softimage, Maya, 3DS Max, modo, etc.). Note that the 3D viewport does not sit in a constant render loop pegged at max FPS. It remains idle (0 FPS) until some action is taking place in the viewport such as moving the viewport camera from side to side using the mouse or pressing play for animation. If the viewport is idle, there is no drain on the CPU / GPU. I'd like to use this technique in a couple of tools I'm working on and possibly within the game.

Is there a generally accepted programming technique with OpenGL and/or DirectX (via C++) that will achieve this?

One solution would be to do a standard render loop and check for input 1000s of times per second, but that seems inefficient and hard to manage as you add more and more "events" which would trigger a updated render.

Please let me know if the question is unclear. If I knew how to describe it perfectly I could probably just search google.

\$\endgroup\$

3 Answers 3

2
\$\begingroup\$

The answer to this question has absolutely nothing to do with OpenGL or DirectX. It is entirely dependent on how you implement your event loop in your window system code.

The OS's windowing system API is built around the concept of an event loop. Whenever the user does something, like press a key or move the mouse, an event is sent from the windowing system to the application.

Typical applications, including 3DS Max, use an event loop something like this pseudo code:

while (WaitForMessage())
{
  ProcessMessage();
  UpdateState();
}

If there are no messages from the windowing system (because the user is not interacting with the application), then WaitForMessage will block (put the application to sleep) until a new message arrives. Hence the application is not using any CPU/GPU Time.

Typical games are full of animations and game logic that is happening whether the player is actively pressing keys or moving the mouse. It would be pretty silly if the AI-controlled enemies only moved while the user was moving the mouse. They use an event loop something like this pseudo code:

while (IsPlaying())
{
  if (PeekMessage())
    ProcessMessage();
  UpdateState();
}

Hence they are constantly running in a loop as fast as possible. This event loop is mandatory, so even games that are doing all input by polling the OS must still have that event loop, since applications are required to respond to and process messages from the windowing system. Otherwise the system will consider your app hung/nonresponsive, and might kill it. Also, there are other special system messages an app must handle that aren't related to user interaction.

That loop is somewhere in the app. If you're using something like GLUT or certain other toolkits, the loop might be hidden away from you. Those toolkits are generally not meant for games or "serious" apps, and I recommend avoiding them and either using native code (e.g. the Win32 API on Windows, X11 on UNIX/Linux systems, etc.) or using more capable wrapper library (SDL being a good example of one).

Note that this is a bit more complex with actual graphics code, since you can use the "sync to v-blank" option to only render as fast as the monitor can display new images. A game using this can still easily achieve well under 100% system utilization, assuming it can update and draw an entire frame much quicker than the monitor's native frame time. More complex 3D apps can struggle to maintain the native 60Hz (for typical monitors) and so will continue to consume near 100% of the processing time.

Things get a bit more complex when you want to have animations in your app, but don't want it to waste resources. Typically, normal desktop applications will use a built-in timer system in the windowing system. These timers will send messages to the application when they fire. So if you have an animation that needs to play at 15 frames per second, you would create a timer in the windowing system set to fire every 1/15th of a second. That first message loop would then receive a timer message every 1/15th of a second, allowing you to update the animation state. When the animation is complete, you would disable the timer so that your application doesn't keep receiving unnecessary timer messages (and hence it can sleep at 0% CPU utilization until the user interacts with it again).

This timer approach can be a little bit hairier. If you have multiple animations, you don't want to create multiple timers in the windowing system; in fact, some windowing systems only allow a single timer or a small number of timers. You'll want to have your own animation manager in your application that can be driven by a single timer message, and which can easily know if there are no pending animations and hence that the timer is unnecessary.

For games, the timer approach is generally not suitable. Windowing system timers are driven by the OS's low-precision timer features, generally, which work well enough for low-framerate animations (like most UI animations) but are not precise enough for the framerates that games want to run at (30 at a minimum, 60+ ideally). This is why games use the loop above.

If you want to make your apps behave like 3DS Max, just use the first kind of event loop.

If you want to make a game that doesn't use 100% CPU/GPU time unnecessarily, do two things. First, use sync to v-blank if your game is capable of rendering a consistent 30+ FPS. That way your app will only update as fast as is necessary. Second, when your game is in a state where there is no animation or AI updates or networking to deal with (say, in the pause screen in a single-player game), simply switch your update loop. You can do it with something like this pseudo code:

while (IsPlaying())
{
   if (IsPaused() && WaitForMessage())
     ProcessMessage();
   else if (IsNotPaused() && PeekMessage())
     ProcessMessage();
   UpdateState();
}

Be careful with things like your time delta with code like this. When you come out of the pause state your time delta will be quite large since UpdateState() was not called at all during the pause state. You may want to skip doing any time-dependent updates on the first frame after unpausing, or simply cap your time delta at some reasonable time (1/15th of a second or so). And of course, always fix your timestep!

\$\endgroup\$
1
  • \$\begingroup\$ Exactly what I was looking for. In the end, it's simply altering the message handling. \$\endgroup\$
    – Inisheer
    Dec 26, 2012 at 2:31
1
\$\begingroup\$

The basic way to do this is just call repaint() when a region of the window is invalidated.

You would trigger a repaint whenever the user performs a GUI action that requires the viewport to be re-rendered.

An old way to write a game in a C# native window (which you don't do now! you use XNA!) is to basically call invalidate at the end of each paint event. This makes for a pretty continuous (but not ideally performing) "game loop".

The GUI and 3D rendering representation generally run on the same thread, so there are no threading issues. Data processing jobs (such as rendering) can be performed on separate worker threads, and they report back to the main thread when they are done.

\$\endgroup\$
0
\$\begingroup\$

There are 2 ways of doing it. One is called 'polling' that is where you check for events continuously in a loop. The other is where you specify a function to fire when an event happens and is refereed to as a 'callback'.

Most Windowing/widget systems will have some kind of callback system in place where you define a function that executes whenever an event like a mouse is clicked, a key pressed, a window is resized, etc... rather than polling it yourself.

If you are designing your own 3D Max grabbing a widget system like Qt would be a good idea (It refers to them as Signals and Slots rather than callbacks though).

All the main windowsing systems, Qt, GLFW, GLUT, SFML, also have callback sytems. (SDL might not, but it does have a function to wait for events that you might be able to setup in it's own thread to simulate the same thing, but I'm not sure how threadsafe SDL is so tread carefully, the wiki page says it needs to be the same thread that set the video mode). DirectX (or rather XInput/DirectInput will probably also have a callback system).

Otherwise if your looking at doing it yourself it will probably be specific to the platform that you are running on. At the real low level there are things called interrupts which are done on the hardware level, basically the processor puts your program to sleep until it receives a hardware interrupt but it's unlikely you will need to bother with anything that low level (even if your on an embedded device like a console/phone there is probably a library for that).

The main issue with callbacks would be ensuring that your program is threadsafe. You might receive a callback at the same time your already half way through updating the screen.

\$\endgroup\$

You must log in to answer this question.

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