Reliable windowed vsync with OpenGL on Windows? - c++

SUMMARY
It seems that vsync with OpenGL is broken on Windows in windowed mode. I've tried different APIs (SDL, glfw, SFML), all with the same result: While the framerate is limited (and consistently around 16-17 ms according to CPU measurements on multiple 60 Hz setups I've tried), and the CPU is in fact sleeping most of the time, frames are very often skipped. Depending on the machine and the CPU usage for things other than rendering, this can be as bad as effectively cutting the frame rate in half. This problem does not seem to be driver related.
How to have working vsync on Windows with OpenGL in windowed mode, or a similar effect with these properties (if I forgot something notable, or if something is not sensible, please comment):
CPU can sleep most of the time
No tearing
No skipped frames (under the assumption that the system is not overloaded)
CPU gets to know when a frame has actually been displayed
DETAILS / SOME RESEARCH
When I googled opengl vsync stutter or opengl vsync frame drop or similar queries, I found that many people are having this issue (or a very similar one), yet there seems to be no coherent solution to the actual problem (many inadequately answered questions on the gamedev stackexchange, too; also many low-effort forums posts).
To summarize my research: It seems that the compositing window manager (DWM) used in newer versions of Windows forces triple buffering, and that interferes with vsync. People suggest disabling DWM, not using vsync, or going fullscreen, all of which are not a solution to the original problem (FOOTNOTE1). I have also not found a detailed explanation why triple buffering causes this issue with vsync, or why it is technologically not possible to solve the problem.
However: I've also tested that this does not occur on Linux, even on VERY weak PCs. Therefore it must be technically possible (at least in general) for OpenGL-based hardware acceleration to have functional vsync enabled without skipping frames.
Also, this is not a problem when using D3D instead of OpenGL on Windows (with vsync enabled). Therefore it must be technically possible to have working vsync on Windows (I have tried new, old, and very old drivers and different (old and new) hardware, although all the hardware setups I have available are Intel + NVidia, so I don't know what happens with AMD/ATI).
And lastly, there surely must be software for Windows, be it games, multimedia applications, creative production, 3D modeling/rendering programs or whatever, that use OpenGL and work properly in windowed mode while still rendering accurately, without busy-waiting on the CPU, and without frame drops.
I've noticed that, when having a traditional rendering loop like
while (true)
{
poll_all_events_in_event_queue();
process_things();
render();
}
The amount of work the CPU has to do in that loop affects the behavior of the stuttering. However, this is most definitely not an issue of the CPU being overloaded, as the problem also occurs in one of the most simple programs one could write (see below), and on a very powerful system that does nothing else (the program being nothing other than clearing the window with a different color on each frame, and then displaying it).
I've also noticed that it never seems to get worse than skipping every other frame (i.e., in my tests, the visible framerate was always somewhere between 30 and 60 on a 60 Hz system). You can observe somewhat of a Nyquist sampling theorem violation when running program that changes the background color between 2 colors on odd and even frames, which makes me believe that something is not synchronized properly (i.e. a software bug in Windows or its OpenGL implementation). Again, the framerate as far as the CPU is concerned is rock solid. Also, timeBeginPeriod has had no noticeable effect in my tests.
(FOOTNOTE1) It should be noted though that, because of the DWM, tearing does not occur in windowed mode (which is one of the two main reasons to use vsync, the other reason being making the CPU sleep for the maximum amount of time possible without missing a frame). So it would be acceptable for me to have a solution that implements vsync in the application layer.
However, the only way I see that being possible is there is a way to explicitly (and accurately) wait for a page flip to occur (with possibility of timeout or cancellation), or to query a non-sticky flag that is set when the page is flipped (in a way that doesn't force flushing the entire asynchronous render pipeline, like for example glGetError does), and I have not found a way to do either.
Here is some code to get a quick example running that demonstrates this problem (using SFML, which I found to be the least painful to get to work).
You should see homogenous flashing. If you ever see the same color (black or purple) for more than one frame, it's bad.
(This flashes the screen with the display's refresh rate, so maybe epilepsy warning):
// g++ TEST_TEST_TEST.cpp -lsfml-system -lsfml-window -lsfml-graphics -lGL
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/OpenGL.hpp>
#include <iostream>
int main()
{
// create the window
sf::RenderWindow window(sf::VideoMode(800, 600), "OpenGL");
window.setVerticalSyncEnabled(true);
// activate the window
window.setActive(true);
int frame_counter = 0;
sf::RectangleShape rect;
rect.setSize(sf::Vector2f(10, 10));
sf::Clock clock;
while (true)
{
// handle events
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
return 0;
}
}
++frame_counter;
if (frame_counter & 1)
{
glClearColor(0, 0, 0, 1);
}
else
{
glClearColor(60.0/255.0, 50.0/255.0, 75.0/255.0, 1);
}
// clear the buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Enable this to display a column of rectangles on each frame
// All colors (and positions) should pop up the same amount
// This shows that apparently, 1 frame is skipped at most
#if 0
int fc_mod = frame_counter % 8;
int color_mod = fc_mod % 4;
for (int i = 0; i < 30; ++i)
{
rect.setPosition(fc_mod * 20 + 10, i * 20 + 10);
rect.setFillColor(
sf::Color(
(color_mod == 0 || color_mod == 3) ? 255 : 0,
(color_mod == 0 || color_mod == 2) ? 255 : 0,
(color_mod == 1) ? 155 : 0,
255
)
);
window.draw(rect);
}
#endif
int elapsed_ms = clock.restart().asMilliseconds();
// NOTE: These numbers are only valid for 60 Hz displays
if (elapsed_ms > 17 || elapsed_ms < 15)
{
// Ideally you should NEVER see this message, but it does tend to stutter a bit for a second or so upon program startup - doesn't matter as long as it stops eventually
std::cout << elapsed_ms << std::endl;
}
// end the current frame (internally swaps the front and back buffers)
window.display();
}
return 0;
}
System info:
Verified this problem on these systems:
Windows 10 x64 i7-4790K + GeForce 970 (verified that problem does not occur on Linux here) (single 60 Hz monitor)
Windows 7 x64 i5-2320 + GeForce 560 (single 60 Hz monitor)
Windows 10 x64 Intel Core2 Duo T6400 + GeForce 9600M GT (verified that problem does not occur on Linux here) (single 60 Hz laptop display)
And 2 other people using Windows 10 x64 and 7 x64 respectively, both "beefy gaming rigs", can request specs if necessary
UPDATE 20170815
Some additional testing I've done:
I tried adding explicit sleeps (via the SFML library, which basically just calls Sleep from the Windows API while ensuring that timeBeginPeriod is minimal).
With my 60 Hz setup, A frame should ideally be 16 2/3 Hz. According to QueryPerformanceCounter measurements, my system is, most of the time, very accurate with those sleeps.
Adding a sleep of 17 ms causes me to render slower than the refresh rate. When I do this, some frames are displayed twice (this is expected), but NO frames are dropped, ever. The same is true for even longer sleeps.
Adding a sleep of 16 ms sometimes causes a frame to be displayed twice, and sometimes causes a frame to be dropped. This is plausible in my opinion, considering a more or less random combination of the result at 17 ms, and the result at no sleep at all.
Adding a sleep of 15 ms behaves very similarly to having no sleep at all. It's fine for a short moment, then about every 2nd frame is dropped. The same is true for all values from 1 ms to 15 ms.
This reinforced my theory that the problem might be nothing other than some plain old concurrency bug in the vsync logic in the OpenGL implementation or the operating system.
I also did more tests on Linux. I hadn't really looked much into it before - I merely verified that the frame drop problem didn't exist there and that the CPU was, in fact, sleeping most of the time. I realised that, depending on several factors, I can make tearing consistenly occur on my test machine, despite vsync. As of yet, I do not know whether this that issue is connected to the original problem, or if it is something entirely different.
It seems like the better approach would be some gnarly workarounds and hacks, and ditching vsync altogether and implementing everything in the application (because apparently in 2017 we can't get the most basic frame rendering right with OpenGL).
UPDATE 20170816
I have tried to "reverse-engineer" a bunch of open source 3D engines (got hung up on obbg (https://github.com/nothings/obbg) in particular).
First, I checked that the problem does not occur there. The frame rate is butter smooth. Then, I added my good old flashing purple/black with the colored rects and saw that the stuttering was indeed minimal.
I started ripping out the guts of the program until I ended up with a simple program like mine. I found that there is some code in obbg's rendering loop that, when removed, causes heavy stutter (namely, rendering the main part of the obbg ingame world). Also, there is some code in the initialization that also causes stutter when removed (namely, enabling multisampling). After a few hours of fiddling around it seems that OpenGL needs a certain amount of workload to function properly, but I have yet to find out what exactly needs to be done. Maybe rendering a million random triangles or something will do.
I also reaslised that all my existing tests behave slightly differently today. It seems that I have overall fewer, but more randomly distributed frame drops today than the days before.
I also created a better demo project that uses OpenGL more directly, and since obbg used SDL, I also switched to that (although I briefly looked over the library implementations and it would surprise me if there was a difference, but then again this entire ordeal is a surprise anyway). I wanted to approach the "working" state from both the obbg-based side, and the blank project side so I can be really sure what the problem is. I just put all the required SDL binaries inside the project; if as long as you have Visual Studio 2017 there should be no additional dependencies and it should build right away. There are many #ifs that control what is being tested.
https://github.com/bplu4t2f/sdl_test
During the creation of that thing I also took another look how SDL's D3D implementation behaves. I had tested this previously, but perhaps not quite extensively enough. There were still no duplicate frames and no frame drops at all, which is good, but in this test program I implemented a more accurate clock.
To my surprise I realised that, when using D3D instead of OpenGL, many (but not the majority) loop iterations take somewhere between 17.0 and 17.2 ms (I would not have caught that with my previous test programs). This does not happen with OpenGL. The OpenGL rendering loop is consistently in the range 15.0 .. 17.0. If it is true that sometimes there needs to be a slightly longer waiting period for the vertical blank (for whatever reason), then OpenGL seems to miss that. That might be the root cause of the entire thing?
Yet another day of literally staring at a flickering computer screen. I have to say I really did not expect to spend that amount of time on rendering nothing but a flickering background and I'm not particularly fond of that.

Related

How to do exactly one render per vertical sync (no repeating, no skipping)?

I'm trying to do vertical synced renders so that exactly one render is done per vertical sync, without skipping or repeating any frames. I would need this to work under Windows 7 and (in the future) Windows 8.
It would basically consist of drawing a sequence of QUADS that would fit the screen so that a pixel from the original images matches 1:1 a pixel on the screen. The rendering part is not a problem, either with OpenGL or DirectX. The problem is the correct syncing.
I previously tried using OpenGL, with the WGL_EXT_swap_control extension, by drawing and then calling
SwapBuffers(g_hDC);
glFinish();
I tried all combinations and permutation of these two instructions along with glFlush(), and it was not reliable.
I then tried with Direct3D 10, by drawing and then calling
g_pSwapChain->Present(1, 0);
pOutput->WaitForVBlank();
where g_pSwapChain is a IDXGISwapChain* and pOutput is the IDXGIOutput* associated to that SwapChain.
Both versions, OpenGL and Direct3D, result in the same: The first sequence of, say, 60 frames, doesn't last what it should (instead of about 1000ms at 60hz, is lasts something like 1030 or 1050ms), the following ones seem to work fine (about 1000.40ms), but every now and then it seems to skip a frame. I do the measuring with QueryPerformanceCounter.
On Direct3D, trying a loop of just the WaitForVBlank, the duration of 1000 iterations is consistently 1000.40 with little variation.
So the trouble here is not knowing exactly when each of the functions called return, and whether the swap is done during the vertical sync (not earlier, to avoid tearing).
Ideally (if I'm not mistaken), to achieve what I want, it would be to perform one render, wait until the sync starts, swap during the sync, then wait until the sync is done. How to do that with OpenGL or DirectX?
Edit:
A test loop of just WaitForVSync 60x takes consistently from 1000.30ms to 1000.50ms.
The same loop with Present(1,0) before WaitForVSync, with nothing else, no rendering, takes the same time, but sometimes it fails and takes 1017ms, as if having repeated a frame. There's no rendering, so there's something wrong here.
I have the same problem in DX11. I want to guarantee that my frame rendering code takes an exact multiple of the monitor's refresh rate, to avoid multi-buffering latency.
Just calling pSwapChain->present(1,0) is not sufficient. That will prevent tearing in fullscreen mode, but it does not wait for the vblank to happen. The present call is asynchronous and it returns right away if there are frame buffers remaining to be filled. So if your render code is producing a new frame very quickly (say 10ms to render everything) and the user has set the driver's "Maximum pre-rendered frames" to 4, then you will be rendering four frames ahead of what the user sees. This means 4*16.7=67ms of latency between mouse action and screen response, which is unacceptable. Note that the driver's setting wins - even if your app asked for pOutput->setMaximumFrameLatency(1), you'll get 4 frames regardless. So the only way to guarantee no mouse-lag regardless of driver setting is for your render loop to voluntarily wait until the next vertical refresh interval, so that you never use those extra frameBuffers.
IDXGIOutput::WaitForVBlank() is intended for this purpose. But it does not work! When I call the following
<render something in ~10ms>
pSwapChain->present(1,0);
pOutput->waitForVBlank();
and I measure the time it takes for the waitForVBlank() call to return, I am seeing it alternate between 6ms and 22ms, roughly.
How can that happen? How could waitForVBlank() ever take longer than 16.7ms to complete? In DX9 we solved this problem using getRasterState() to implement our own, much-more-accurate version of waitForVBlank. But that call was deprecated in DX11.
Is there any other way to guarantee that my frame is exactly aligned with the monitor's refresh rate? Is there another way to spy the current scanline like getRasterState used to do?
I previously tried using OpenGL, with the WGL_EXT_swap_control extension, by drawing and then calling
SwapBuffers(g_hDC);
glFinish();
That glFinish() or glFlush is superfluous. SwapBuffers implies a glFinish.
Could it be, that in your graphics driver settings you set "force V-Blank / V-Sync off"?
We use DX9 currently, and want to switch to DX11. We currently use GetRasterState() to manually sync to the screen. That goes away in DX11, but I've found that making a DirectDraw7 device doesn't seem to disrupt DX11. So just add this to your code and you should be able to get the scanline position.
IDirectDraw7* ddraw = nullptr;
DirectDrawCreateEx( NULL, reinterpret_cast<LPVOID*>(&ddraw), IID_IDirectDraw7, NULL );
DWORD scanline = -1;
ddraw->GetScanLine( &scanline );
On Windows 8.1 and Windows 10, you can make use of the DXGI 1.3 DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT. See MSDN. The sample here is for Windows 8 Store apps, but it should be adaptable to class Win32 windows swapchains as well.
You may find this video useful as well.
When creating a Direct3D device, set PresentationInterval parameter of the D3DPRESENT_PARAMETERS structure to D3DPRESENT_INTERVAL_DEFAULT.
If you run in kernel-mode or ring-0, you can attempt to read bit 3 from the VGA input register (03bah,03dah). The information is quite old but although it was hinted here that the bit might have changed location or may be obsoleted in later version of Windows 2000 and up, I actually doubt this. The second link has some very old source-code that attempts to expose the vblank signal for old Windows versions. It no longer runs, but in theory rebuilding it with latest Windows SDK should fix this.
The difficult part is building and registering a device driver that exposes this information reliably and then fetching it from your application.

SDL_SetVideoMode ignores flags & SDL_Flip is slow

I'm initializing with
SDL_Init(SDL_INIT_VIDEO);
screen = SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE|SDL_DOUBLEBUF);
and then calling, in a loop, between calls to sleep, just
SDL_LockSurface(screen);
// Will eventually twiddle pixels here...but not yet
SDL_UnlockSurface(screen);
SDL_Flip(screen);
And that call to flip takes a varying amount of time, but around 10ms, which is a lot for nothing.
So that makes me wonder if I'm causing, say, a copy from video memory to system memory or something, and I should create the surface differently. BUT, additionally, screen->flags is always equal to SDL_ASYNCBLIT and no other bits are set, regardless of the flags I pass to SDL_SetVideoMode. So I can't make any other kind of surface anyway.
Should I be creating another offscreen surface, rendering to that, and then blitting it to the screen? What am I doing wrong?
EDIT: Removing the SDL_Lock and SDL_Unlock pair does nothing to speed things up. The SDL_Flip is just slow.
For the sake of the two or three people that may, someday, see this question, I think the reason the SDL_Flip takes so long is that it waits for vsync, so a busy loop calling SDL_Flip will necessarily be limited by the vsync rate.
If that's correct, the fact that SDL_Flip takes so long is not an actual issue, since if I were doing work to render things, then there'd be less time to wait for the vsync.
I use Win7, Codeblocks/MingW, SDL 1.2.14. Grumdrig said:
For the sake of the two or three people that may, someday, see this
question, I think the reason the SDL_Flip takes so long is that it
waits for vsync, so a busy loop calling SDL_Flip will necessarily be
limited by the vsync rate.
If that's correct, the fact that SDL_Flip takes so long is not an
actual issue, since if I were doing work to render things, then
there'd be less time to wait for the vsync.
Nah, something is wrong. My programs performed fine until relatively recently, and I spent awhile myself today looking for the cause of my own bottleneck, which turned out to be SDL_Flip() like yours, which led me to your question through a google search. Interestingly enough, on my older WinXP single core the performance is better, so my guess is that the problem is caused by the friendly Win7 auto-updates. Perhaps Microsoft is unintentionally de-optimizing the GDI!
In any case, SDL_Flip shouldn't take so long to resolve itself, and in case anyone is wondering, I put a Uint32 SDL_Getticks() timer above SDL_Flip() and outputted the length below if it was over 5 ms. The result was numbers like 27, 59 and 88 milliseconds from time to time, which is not good. And of course, the problem is the same whether I use SWSURFACE or HWSURFACE as the flag to SetVideoMode.
Before I leave a comment on the SDL website I may try rebuilding with SDL 1.2.15, though that might require a rebuild of SpriG, so I'm not in a rush. But in general, I stand by what I said: chances are, it's a Win7 thing, if that's your operating system.
Addendum 5:17 PM EST: I just compiled and ran a smaller, unrelated project that uses SDL 1.2.15 and the problem was the same: normal smooth movement followed by occasional jerks. Testing for only when SDL_Flip() took more than 5 milliseconds, the output included values ranging from 30 to over 60 milliseconds, which would happen about once every second or two.
Addendum 5-8-12: On 5-4-12 there was a big thunderstorm and I had to turn off and unplug the computer, with the result that the next day SDL_Flip() always took under 5ms, so the problem is solved now.

Constantly lag in opengl application

I'm getting some repeating lags in my opengl application.
I'm using the win32 api to create the window and I'm also creating a 2.2 context.
So the main loop of the program is very simple:
Clearing the color buffer
Drawing a triangle
Swapping the buffers.
The triangle is rotating, that's the way I can see the lag.
Also my frame time isn't smooth which may be the problem.
But I'm very very sure the delta time calculation is correct because I've tried plenty ways.
Do you think it could be a graphic driver problem?
Because a friend of mine run almost the exactly same program except I do less calculations + I'm using the standard opengl shader.
Also, His program use more CPU power than mine and the CPU % is smoother than mine.
I should also add:
On my laptop I get same lag every ~1 second, so I can see some kind of pattern.
There are many reasons for a jittery frame rate. Off the top of my head:
Not calling glFlush() at the end of each frame
other running software interfering
doing things in your code that certain graphics drivers don't like
bugs in graphics drivers
Using the standard windows time functions with their terrible resolution
Try these:
kill as many running programs as you can get away with. Use the process tab in the task manager (CTRL-SHIFT-ESC) for this.
bit by bit, reduce the amount of work your program is doing and see how that affects the frame rate and the smoothness of the display.
if you can, try enabling/disabling vertical sync (you may be able to do this in your graphic card's settings) to see if that helps
add some debug code to output the time taken to draw each frame, and see if there are anomalies in the numbers, e.g. every 20th frame taking an extra 20ms, or random frames taking 100ms.

Weird CPU usage in OpenGL program

In an MFC-program I built myself I have some weird problems with the CPU usage.
I load a point cloud of around 360k points and everything works fine (I use VBO buffers which is the way to do it from what I understand?). I can move it around as I please and notice no adverse effects (CPU usage is very low, GPU does all the work). But then at certain angles and zoom values I see the CPU spike on one of my processors! I can then change the angle or zoom a little and it will go down to around 0 again. This is more likely to happen in a large window than a small one.
I measure the FPS of the program and it's constantly at 65, but when the CPU spike hits it typically goes down around 10 units to 55. I also measure the time SwapBuffers take and during normal operation it's around 0-1 ms. Once the CPU spike hits it goes up to around 20 ms, so it's clear something suddenly gets very hard to calculate in that function (for the GPU I guess?). This something is not in the DrawScene function (which is the function one would expect to eat CPU in a poor implementation), so I'm at a bit of a loss.
I know it's not due to the number of points visible because this can just as easily happen on just a sub-section of the data as on the whole cloud. I've tried to move it around and see if it's related to the depth buffer, clipping or similar but it seems entirely random what angles create the problem. It does seem somewhat repeatable though; moving the model to a position that was laggy once will be laggy when moved there again.
I'm very new at OpenGL so it's not impossible I've made some totally obvious error.
This is what the render loop looks like (it's run in an MFC app via a timer event with 1 ms period):
// Clear color and depth buffer bits
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Draw OpenGL scene
OGLDrawScene();
unsigned int time1 = timeGetTime();
// Swap buffers
SwapBuffers(hdc);
// Calculate execution time for SwapBuffers
m_time = timeGetTime() - time1;
// Calculate FPS
++m_cnt;
if (timeGetTime() - m_lastTime > 1000)
{
m_fps = m_cnt;
m_cnt = 0;
m_lastTime = timeGetTime();
}
I've noticed that (at least a while back), ATI drivers tend to like to spin-wait a little too aggressively, while NVidia drivers tend to be interrupt driven. Technically spin-waiting is faster, assuming you have nothing better to do. Unfortunately, today you probably do have something better to do on another thread.
I think the OP's display drivers may indeed be spin-waiting.
Ok, so I think I have figured out how this can happen.
To begin with the WM_TIMER messages doesn't seem to be generated more often than every 15 ms at best, even when using timeBeginPeriod(1), at least on my computer. This resulted in the standard 65 fps I was seeing.
As soon as the scene took more than 15 ms to render, SwapBuffers would be the limiting factor instead. SwapBuffers seem to busy-wait, which resulted in 100% CPU usage on one core when this happened. This is not something that occurred at certain camera angles, but is a result of a fluidly changing fps depending on how many points were shown on the screen at the time. It just appeared to spike whenever rendering happened to hit a time over 15 ms and started to wait at SwapBuffers instead.
On a similar note, does anyone know of a function like "glReadyToSwap" or something like that? A function that indicates if the buffers are ready to be swapped? That way I could choose another method for rendering with a higher resolution (1 ms for example) and then each ms check if the buffers are ready to swap, if they aren't just wait another ms, so as to not busy-wait.

setting max frames per second in openGL

Is there any way to calculate how much updates should be made to reach desired frame rate, NOT system specific? I found that for windows, but I would like to know if something like this exists in openGL itself. It should be some sort of timer.
Or how else can I prevent FPS to drop or raise dramatically? For this time I'm testing it on drawing big number of vertices in line, and using fraps I can see frame rate to go from 400 to 200 fps with evident slowing down of drawing it.
You have two different ways to solve this problem:
Suppose that you have a variable called maximum_fps, which contains for the maximum number of frames you want to display.
Then You measure the amount of time spent on the last frame (a timer will do)
Now suppose that you said that you wanted a maximum of 60FPS on your application. Then you want that the time measured be no lower than 1/60. If the time measured s lower, then you call sleep() to reach the amount of time left for a frame.
Or you can have a variable called tick, that contains the current "game time" of the application. With the same timer, you will incremented it at each main loop of your application. Then, on your drawing routines you calculate the positions based on the tick var, since it contains the current time of the application.
The big advantage of option 2 is that your application will be much easier to debug, since you can play around with the tick variable, go forward and back in time whenever you want. This is a big plus.
Rule #1. Do not make update() or loop() kind of functions rely on how often it gets called.
You can't really get your desired FPS. You could try to boost it by skipping some expensive operations or slow it down by calling sleep() kind of functions. However, even with those techniques, FPS will be almost always different from the exact FPS you want.
The common way to deal with this problem is using elapsed time from previous update. For example,
// Bad
void enemy::update()
{
position.x += 10; // this enemy moving speed is totally up to FPS and you can't control it.
}
// Good
void enemy::update(elapsedTime)
{
position.x += speedX * elapsedTime; // Now, you can control its speedX and it doesn't matter how often it gets called.
}
Is there any way to calculate how much updates should be made to reach desired frame rate, NOT system specific?
No.
There is no way to precisely calculate how many updates should be called to reach desired framerate.
However, you can measure how much time has passed since last frame, calculate current framerate according to it, compare it with desired framerate, then introduce a bit of Sleeping to reduce current framerate to the desired value. Not a precise solution, but it will work.
I found that for windows, but I would like to know if something like this exists in openGL itself. It should be some sort of timer.
OpenGL is concerned only about rendering stuff, and has nothing to do with timers. Also, using windows timers isn't a good idea. Use QueryPerformanceCounter, GetTickCount or SDL_GetTicks to measure how much time has passed, and sleep to reach desired framerate.
Or how else can I prevent FPS to drop or raise dramatically?
You prevent FPS from raising by sleeping.
As for preventing FPS from dropping...
It is insanely broad topic. Let's see. It goes something like this: use Vertex buffer objects or display lists, profile application, do not use insanely big textures, do not use too much alpha-blending, avoid "RAW" OpenGL (glVertex3f), do not render invisible objects (even if no polygons are being drawn, processing them takes time), consider learning about BSPs or OCTrees for rendering complex scenes, in parametric surfaces and curves, do not needlessly use too many primitives (if you'll render a circle using one million polygons, nobody will notice the difference), disable vsync. In short - reduce to absolute possible minimum number of rendering calls, number of rendered polygons, number of rendered pixels, number of texels read, read every available performance documentation from NVidia, and you should get a performance boost.
You're asking the wrong question. Your monitor will only ever display at 60 fps (50 fps in Europe, or possibly 75 fps if you're a pro-gamer).
Instead you should be seeking to lock your fps at 60 or 30. There are OpenGL extensions that allow you to do that. However the extensions are not cross platform (luckily they are not video card specific or it'd get really scary).
windows: wglSwapIntervalEXT
x11 (linux): glXSwapIntervalSGI
max os x: ?
These extensions are closely tied to your monitor's v-sync. Once enabled calls to swap the OpenGL back-buffer will block until the monitor is ready for it. This is like putting a sleep in your code to enforce 60 fps (or 30, or 15, or some other number if you're not using a monitor which displays at 60 Hz). The difference it the "sleep" is always perfectly timed instead of an educated guess based on how long the last frame took.
You absolutely do wan't to throttle your frame-rate it all depends on what you got
going on in that rendering loop and what your application does. Especially with it's
Physics/Network related. Or if your doing any type of graphics processing with an out side toolkit (Cairo, QPainter, Skia, AGG, ...) unless you want out of sync results or 100% cpu usage.
This code may do the job, roughly.
static int redisplay_interval;
void timer(int) {
glutPostRedisplay();
glutTimerFunc(redisplay_interval, timer, 0);
}
void setFPS(int fps)
{
redisplay_interval = 1000 / fps;
glutTimerFunc(redisplay_interval, timer, 0);
}
Here is a similar question, with my answer and worked example
I also like deft_code's answer, and will be looking into adding what he suggests to my solution.
The crucial part of my answer is:
If you're thinking about slowing down AND speeding up frames, you have to think carefully about whether you mean rendering or animation frames in each case. In this example, render throttling for simple animations is combined with animation acceleration, for any cases when frames might be dropped in a potentially slow animation.
The example is for animation code that renders at the same speed regardless of whether benchmarking mode, or fixed FPS mode, is active. An animation triggered before the change even keeps a constant speed after the change.