如何稳定帧率

0

我有一个双线程的窗口程序,其中一个线程会不定期向另一个线程发出重绘命令(通过SendMessage)

我想知道要怎么处理才能使帧数维持在60帧,即当重绘命令过于密集时,我需要怎么做才能适当地忽略掉一部分命令,而又不漏掉必要的命令使画面卡顿?

想过在 WndProc 用 Timer,但是听说精度不怎么样,如果直接开第三个线程的话又感觉太麻烦,我想知道能不能直接在 WndProc 层把这一问题搞定。

ava
无名氏

2021-8-25

0

如果你的60帧是手动模拟的,可以参考以下思路:

bool 需要重绘=false;
while(1){
	while(有命令){	// 这里参考了windows对WM_PAINT消息的折叠优化(即此消息不会堆积)
		switch(获取命令()){
		case 重绘命令:
			需要重绘=true;
			break;
		default:
			break;
		}
	}
	if(需要重绘){ // 此if的细节你可以自行调整
		// 注:一般来说,有了上面的优化,程序既能以最高帧率运行,又能很大程度上减少资源浪费
		// 但如果你对帧率有其它要求(如不希望程序超过100帧),则可使用下一行代码
		等待直到时间对齐至60帧(); // 可使用Sleep+时间判断/计时器实现
		// 注:可选使用timeBeginPeriod提升Sleep、计时器等的精度,但可能会显著缩短续航
		// 建议做法是降低对精度的要求(或放弃60帧这个数字)
		重绘();
		需要重绘=false;
	}else{
		等待命令到来();
	}
}

如果你的60帧指的是显卡的垂直同步:
代码类似上面,但 等待直到时间对齐至60帧() 这个函数需要替换为别的函数,不过关于等待垂直同步并没有一个十分完美的解决方案,这里提供几个可能性:

  1. 调用Direct3D或者DXGI swapchain的Present函数 (合并了等待和重绘两步;大多数使用dx或者angle(opengl)进行渲染的程序使用的方法,推荐)
  2. DwmFlush (用法最简单;firefox曾经使用的方法,不推荐)
  3. IDXGIOutput::WaitForVBlank (chrome和firefox使用的方法(但主要用于javascript的执行,实际渲染走的是方法1或者DirectComposition))
  4. D3DKMTWaitForVerticalBlankEvent (内核层使用的方法,chrome中有相关实现)
ava
勿忘我

2021-8-30

已经用上类似的方法实现了稳定帧率运行,另外问一个问题,相较于使用 SendMessage 发送 UserMessage,直接通过线程间修改重绘变量的通信效率快得多,这是我操作有误还是原本就这个样子? -  无名氏  2021-8-30
0

既然你稳定在 60 帧,那就肯定有计时器呗,自己计时,间隔小于一定时间的就忽略。

而且,你在写什么?

如果你在写普通窗口程序,没有哪个窗口程序是反复刷新的。UI 一般是有消息立刻刷新。

如果你在写游戏,也没有哪个程序是靠另一个线程来通知刷新的。常见的游戏主循环有两种,一种是能跑多少帧就跑多少帧,另一种是延时以确保多少帧刷新一次。都是在一个线程里。

ava
慢羊羊

2021-8-25

想用EasyX模拟一个控制台来弥补我在 windows 下没办法关掉输出缓冲区自动刷新以及字体和颜色控制问题。窗口刷新当且仅当发送 flush 或是任何输入命令,同时我又想保证在大量发送 flush (比如从 0 输出到 1e6,每次输出都刷新)的情况下保证其显示效率。 -  无名氏  2021-8-29
技术讨论社区
相关提问