现在,在我的OpenGL游戏引擎中,当我的渲染线程完全无关时,它会占用我的CPU可以提供的最大值。 Windows任务管理器显示我的应用程序占用了25%的处理(我有4个硬件线程,因此25%是一个线程可以采取的最大值)。当我完全没有启动渲染线程时,我得到0-2%(由于它所做的一切正在运行SDL输入循环,所以它自己很担心)。
那么,我的渲染线程到底在做什么?这是一些代码:
Timer timer;
while (gVar.running)
{
timer.frequencyCap(60.0);
beginFrame();
drawFrame();
endFrame();
}
让我们来看看每一个。 Timer
是我使用SDL_GetPerformanceCounter
制作的自定义计时器类。 timer.frequencyCap(60.0);
旨在确保循环每秒运行超过60次。这是Timer::frequencyCap()
的代码:
double Timer::frequencyCap(double maxFrequency)
{
double duration;
update();
duration = _deltaTime;
if (duration < (1.0 / maxFrequency))
{
double dur = ((1.0 / maxFrequency) - duration) * 1000000.0;
this_thread::sleep_for(chrono::microseconds((int64)dur));
update();
}
return duration;
}
void Timer::update(void)
{
if (_freq == 0)
return;
_prevTicks = _currentTicks;
_currentTicks = SDL_GetPerformanceCounter();
// Some sanity checking here. //
// The only way _currentTicks can be less than _prevTicks is if we've wrapped around to 0. //
// So, we need some other way of calculating the difference.
if (_currentTicks < _prevTicks)
{
// If we take difference between UINT64_MAX and _prevTicks, then add that to _currentTicks, we get the proper difference between _currentTicks and _prevTicks. //
uint64 dif = UINT64_MAX - _prevTicks;
// The +1 here prvents an off-by-1 error. In truth, the error would be pretty much indistinguishable, but we might as well be correct. //
_deltaTime = (double)(_currentTicks + dif + 1) / (double)_freq;
}
else
_deltaTime = (double)(_currentTicks - _prevTicks) / (double)_freq;
}
接下来的3个功能相当简单(在此阶段):
void Renderer::beginFrame()
{
// Perform a resize if we need to. //
if (_needResize)
{
gWindow.getDrawableSize(&_width, &_height);
glViewport(0, 0, _width, _height);
_needResize = false;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
void Renderer::endFrame()
{
gWindow.swapBuffers();
}
void Renderer::drawFrame()
{
}
渲染线程是使用std :: thread创建的。我能想到的唯一解释是timer.frequencyCap
不知何故不起作用,除了我在主线程中使用完全相同的函数而且我在0-2%时闲置。
我在这里做错了什么?
答案 0 :(得分:4)
如果启用了V-Sync并且您的程序遵循交换间隔,那么您看到程序占用100%实际上是Windows测量CPU时间的工件。这是一个众所周知的问题,但是任何时候你的程序在驱动程序上下文中阻塞(这是在V-Sync上的OpenGL阻塞时会发生这种情况)窗口会为实际消耗CPU时间的程序计算这个问题,而实际上它只是闲置。
如果在交换缓冲区之后立即添加Sleep(1)
,它将欺骗Windows进入更合理的会计;在某些系统上,即使是Sleep(0)
,也可以做到这一点。
无论如何,100%只是一个美容问题,大部分时间都是。
在过去的几周里,我对低延迟渲染进行了一些详尽的研究(即最小化用户输入和相应光子从显示器出来之间的时间),因为我很快就会得到一个VR耳机。这就是我发现的关于时序SwapBuffers的问题:该问题的理智解决方案实际上是对帧渲染时间进行计时并在SwapBuffers
之前添加一个人工睡眠,以便在V-Sync之前仅唤醒几毫秒。然而,这说起来容易做起来难,因为OpenGL是高度异步的,显式添加同步会降低吞吐量。
答案 1 :(得分:1)
如果您有复杂场景或非优化渲染
试试这个:
尝试测量处理时间
beginFrame();
drawFrame();
endFrame();
检查GL错误
我的代码中没有看到任何glFlush()/ glFinish()
如果你无法解决这个问题,你仍然可以使用像
这样的肮脏技巧Sleep(1);
添加到您的代码this_thread::sleep_for(chrono::microseconds((int64)dur));