SwapBuffers is not busy waiting, it just blocks your thread in the driver context, which makes Windows calculating the CPU usage wrongly: Windows calculates the CPU usage by determining how much CPU time the idle process gets + how much time programs don't spend in driver context. SwapBuffers will block in driver context and your program obviously takes away that CPU time from the idle process. But your CPU is doing literally nothing in the time, the scheduler happily waiting to pass the time to other processes. The idle process OTOH does nothing else than immediately yield its time to the rest of the system, so the scheduler jumps right back into your process, which blocks in the driver what Windows counts as "is clogging CPU". If you'd measure the actual power consumption or heat output, for a simple OpenGL program this will stay rather low.
This irritating behaviour is actually an OpenGL FAQ!
Just create additional threads for parallel data processing. Keep OpenGL in one thread, the data processing in the other. If you want to get down the reported CPU usage, adding a Sleep(0) or Sleep(1) after SwapBuffers will do the trick. The Sleep(1) will make your process spend blocking a little time in user context, so the idle process gets more time, which will even out the numbers. If you don't want to sleep, you may do the following:
const float time_margin = ... // some margin
float display_refresh_period; // something like 1./60. or so.
void render(){
float rendertime_start = get_time();
render_scene();
glFinish();
float rendertime_finish = get_time();
float time_to_finish = rendertime_finish - rendertime_start;
float time_rest = fmod(render_finish - time_margin, display_refresh_period);
sleep(time_rest);
SwapBuffers();
}
In my programs I use this kind of timing but for another reason: I let SwapBuffers block without any helper Sleeps, however I give some other worker threads about that time to do stuff on the GPU through shared context (like updating textures) and I have the garbage collector running. It's not really neccesary to exactly time it, but the worker threads being finished just before SwapBuffers returns allows one to start rendering the next frame almost immediately since most mutexes are already unlocked then.