SwapBuffers的等待,虚伪的FPS(转)
FPS在实时渲染中扮演着一个重要的角色,也许你会去笑一个不懂FPS是什么的游戏新手,但也许,这只是五十步笑一百步罢了。你能读懂SwapBuffers的深情等待吗?——ZwqXin.com
frames per second(FPS,
帧率),作为渲染效率的一种衡量,反映的是整个程序在当前的一个渲染状态下平均每秒所能容纳的“渲染循环”执行次数,也表征了平均每个“渲染循环”(帧)
所用的时间。就表面来看,很多人觉得只要在一个每帧运行一次的函数内设置一个计数器,这样计算出一秒内的记数,便可作为该1s瞬间的FPS了;同样也很容
易能求出平均FPS。
我一直都是这么做的。但是我一直以来有个难解的地方:为什么我的程序的FPS基本不会大于75呢?恩,以前做的DEMO,如果是有计算FPS的,都是这
样:场景复杂的时候它可以降低到1或者直接0掉,但是对于简单的场景它永远只有72左右的FPS;再简单,也是72左右;再再简单,还是72左右……可以
说是“封顶”了。今天我(终于?)想要弄清楚是什么原因了,有以下可能:1.所用的程序框架的某处给它限FPS了;2.初始化过程中沾染了些什么会约束渲
染效率的东西;3.渲染循环体内有一个“每个程序都会用到的函数”是“每个程序的瓶颈”,4.FPS计算的算法有问题;5.其他(啥?)。
于是我把整个渲染流程核查一次,先把消息处理和循环部分弄成一般WIN32的形式,貌似无关……然后又检查像素格式,直接把最佳像素格式设置为10,(某
些程序DEMO中设计者会轮巡所有像素格式找出最佳匹配的,于是我也容易知道对我最“友好”的像素格式是10号……)貌似也没啥。另外,检查FPS计算的
式子,没发现异常。我又把渲染函数RenderGLScene内的东西一个个注释……才发现,只有当我注释了SwapBuffers时,屏幕显示死掉了,
但FPS上到了3000以上……对比72的FPS我还是比较相信这个——所以,是双缓冲的关系么。在像素设置中改像素描述器的
PFD_DOUBLEBUFFER为PFD_SWAP_COPY、PFD_SWAP_EXCHANGE之类的,这下FPS也是很高,但是屏幕猛闪~ - -
。google吧,步小心发现了一个东西:vsync。诶?甘熟噶?
在学Irrlicht引擎的时候,建立DEVICE的时候有一个参数选项:vsync,vertical
syncronisation,API解释得不清楚,就知道跟屏幕有关……屏幕?屏幕刷新率?我突然想起我做图像处理的WIN32程序时的一个窘
况:release出来的程序在人家面前演示,发现图片/视频出不来,后来明白是屏幕刷新率的问题——屏幕刷新率太高会把WIN32循环对屏幕窗口所做的
东西“消灭”掉。那么,这里呢?好,我去看文章了:vertical syncronisation。
垂直同步。原来,程序每一帧的函数全部执行完之后,还要等屏幕刷新了才去执行下一帧啊!一查屏幕刷新率——难怪是72左右呀,正因为偶电脑上设置的屏幕刷
新率是72!恩,这里头肯定有个像sleep那样有等待功能的函数做帮凶——噢,亲爱的SwapBuffers,是你呀~
曾经有人说SwapBuffers比glut库的glutSwapBuffers效率低不少(见这里),但这里不是这个问题。无论是
SwapBuffers还是glutSwapBuffers还是wglSwapBuffers,它们位于渲染体的末端,都有这么个“功能”:当显卡的垂直
同步功能vsyn被置为TRUE时,每一帧渲染完后来到SwapBuffers,都要悲情地等待下一次屏幕刷新的时刻到来,再交换前后缓冲并开始执行下一
帧……上面那篇文章用到了WGL_EXT_swap_control扩展,利用wglSwapIntervalEXT来设置“等待的间隔”。间隔是指一个
屏幕刷新的周期,譬如屏幕刷新率是72Hz,那么一个周期就是(1/72)s,
wglSwapIntervalEXT(1)表明要让SwapBuffers类函数开始执行后要等到下一个屏幕刷新时才返回——然后继续下一帧的执行。
wglSwapIntervalEXT(2)就是等下个再下个屏幕刷新了。这么说,wglSwapIntervalEXT(0),哈,就是不用等——关闭
垂直同步Vsync。
这里安插一个问题,那么如果程序完成一帧本来就可能大于这个屏幕刷新间隔(1/72s)呢?譬如设开始时刻屏幕刚刷新而某帧也同时开始,均设为0,那么程
序的函数集将在第一次刷新之后才完结,进入SwapBuffers。如果没理解错,按照该扩展的spec[EXT_swap_control]所说的话,
之后这个SwapBuffers要一直等待到第二次刷新才返回并交换前后缓冲,开始第二帧——即相当于一帧的“理论的完结”需要两个刷新周期(2/72s
=
1/36s),就是说一帧用时稍微比屏幕刷新周期长一点点,也会导致FPS减小一半……而实际上这种不连续性在我以前的“低帧率”程序中没有体现过。究竟
是怎样的呢?留个疑问。
在我的程序中应用此拓展,在程序初始化阶段就关闭Vsync。哈,很高的FPS。文章vertical syncronisation末尾也提供了该作者写的helper类,很容易理解和应用。好了,这样……等待的结束?还没呢——虚伪的FPS。
是的,这个FPS很虚伪。不是说它太高了(因为我本身对这种大数值的没概念),而是它本身就不是FPS,它不符合我们所想的FPS概念(本文开始处)。更确切点,以上所说的“帧”的概念与FPS中的帧的概念不一样。
我们调用函数控制GPU做事,并不是CPU上这种步步执行-返回-下句的形式。我们是按顺序把函数一个一个(作为地址)输送到显卡某个缓存区内,积累一定
量后再让显卡按此顺序执行。所以,代码的执行完成并不代表显卡上相应的硬件功能执行完成。(其实要是显卡看一个执行一个,流水线不就坏掉了么。)也就这个
原因,可以认为两者是不同步的,有延误等等。那么之前讨论中提及的“帧”就是前者的完成时间,而我们最想要的应该是后者的完成时间——包括CPU向GPU
传递函数所用时间和GPU上实际执行函数功能的用时,可以想象两者应该有很大部分的用时是互相重合的,且后者无论如何还是会在函数全部传递完成后仍然需要
额外时间来完成。这部分额外时间取决于之前传入的是哪类函数、GPU具体执行的是什么功能——如果直接关掉垂直同步Vsync得出FPS,这个FPS就没
有包含这些额外的处理时间,仅仅最多能表示两个U之间数据传输的速度。
所以要让FPS反映真实的每帧——包括数据传输和实际执行、绘图——的用时的话,不能用此法。在glut 教學 - 計算
frame rate 的正確方法 ——
程序设计俱乐部一文中,作者ma_hty(白老鼠(Gary))提供了一种方法,在计算帧率时,用glFinish代替SwapBuffers。它没有
SwapBuffers那种等待的耐心,强制告诉程序和GPU:要结束了,快把剩余的所有函数指令执行完,然后换下一帧。这样的一帧正是“帧”的正确含
义。但是这样将失去双缓冲,屏幕是不会正常显示绘制的东西的,所以不可能用做实时的FPS计算,只用于某时刻用一下,指示当前的真实FPS。
转载自ZwqXin http://www.zwqxin.com/
原文地址:http://www.zwqxin.com/archives/opengl/swapbuffers-fps-vsync.html
SwapBuffers的等待,虚伪的FPS(转)的更多相关文章
- WaitForTargetFPS
WaitForTargetFPS,是关于帧数限制的,你可能开了垂直同步,其实是防止撕裂.先说撕裂,在显示器的帧缓存会被不同步的显卡的帧缓存给替换掉,导致显示器显示到一半的时候,内存被换掉,你看到上频是 ...
- 屏幕分辨率与FPS
屏幕分辨率 刷新率分为垂直刷新率和水平刷新率,一般提到的刷新率通常指垂直刷新率. 垂直刷新率表示屏幕的图象每秒钟重绘多少次,也就是每秒钟屏幕刷新的次数,以Hz(赫兹)为单位. 刷新率越高越好,图象就越 ...
- UE4编程之C++创建一个FPS工程(二)角色网格、动画、HUD、子弹类
转自:http://blog.csdn.net/u011707076/article/details/44243103 紧接上回,本篇文章将和大家一同整理总结UE4关于角色网格.动画.子弹类和HUD的 ...
- android应用程序fps meter[帧数显示]的分析 —— 浅谈root的风险 (3)
上节已经详细说了下注入过程,最后寄生进程在宿主进程中下了个蛋,这下完的蛋有什么作用呢?接下来再具体分析一下. lib0的感染过程分析 对于本例注入的so动态库,首先看一下so的符号: $ readel ...
- "她等待刀尖已经太久"--茨维塔耶娃诗抄
生活 1 你无法夺走我的红晕—— 它强大——如同河水的汛潮! 你是猎人,可我不会上当, 你若追逐,我就会逃跑. 你无法夺走我鲜活的灵魂! 就这样,在急遽的追逐中—— 一匹阿拉伯的骏马, 微 ...
- FPS游戏服务器设计的问题 【转】
一.追溯 去gameloft笔试,有一个题目是说: 叫你去设计一个FPS(第一人称射击游戏),你是要用TCP呢还是要用UDP,说明理由 . 二.学习 这是两篇网上找到的文章,写非常不错. 当时笔试的时 ...
- (转)FPS游戏服务器设计的问题
FPS游戏服务器设计的问题出处:http://www.byteedu.com/thread-20-1-1.html一.追溯 去gameloft笔试,有一个题目是说: 叫你去设计一个FPS(第一人称射击 ...
- 4、APP FPS测试
什么是FPS FPS是图像领域中的定义,是指画面每秒传输帧数,通俗来讲就是指动画或视频的画面数.FPS是测量用于保存.显示动态视频的信息数量.每秒钟帧数愈多,所显示的动作就会愈流畅.通常,要避免动作不 ...
- java 线程 Lock 锁使用Condition实现线程的等待(await)与通知(signal)
一.Condition 类 在前面我们学习与synchronized锁配合的线程等待(Object.wait)与线程通知(Object.notify),那么对于JDK1.5 的 java.util.c ...
随机推荐
- springmvc获取资源文件的两种方式(超简单)
1 比如我们在sc目录下新建一个db.properties文件内容如下 DriverClass=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306 ...
- JAVA HttpClient进行POST请求(HTTPS)
目前,要为另一个项目提供接口,接口是用HTTP URL实现的,最初的想法是另一个项目用jQuery post进行请求. 但是,很可能另一个项目是部署在别的机器上,那么就存在跨域问题,而jquery的p ...
- Windows2008 IIS + .NET环境搭建指南
Windows下最常用的网页服务器是自带的IIS,这里将为大家演示,windows2008下如何搭建IIS + .NET的动态网页环境. 环境配置:Qcloud 云服务器 windows 200864 ...
- 【校招面试 之 剑指offer】第10-2题 青蛙跳台阶问题
题目1:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶.求该青蛙跳上一个n级台阶共有多少种跳法? 题目2:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶...也可以一次跳n级台阶.求该青蛙跳上一个 ...
- BIOS设置找不到设置U盘启动
今天上午弄了好久,BIOS设置找不到设置U盘启动,后来改了一个选项突然就可以了,或许有时候是这个地方的问题 advanced bios features-->interrupt 19 captu ...
- Spring整合JMS——事务管理
Spring提供了一个JmsTransactionManager用于对JMS ConnectionFactory做事务管理.这将允许JMS应用利用Spring的事务管理特性.JmsTransactio ...
- JAVA list集合两种去重方法
结果: 转载地址:http://geek.csdn.net/news/detail/127940
- linux-Centos 7下mysql 5.7.9的rpm包安装
操作系统:Centos 7.1 mysql数据库版本:mysql5.7.18 1.安装新版mysql之前,我们需要将系统自带的mariadb-lib卸载 [root@123 ~]# rpm -qa|g ...
- Luogu 3953[NOIP2017] 逛公园 堆优化dijkstra + 记忆化搜索
题解 首先肯定是要求出单源最短路的,我用了堆优化dijikstra ,复杂度 mlogm,值得拥有!(只不过我在定义优先队列时把greater 打成了 less调了好久 然后我们就求出了$i$到源点的 ...
- 关于UI设计的一些工作了解
关于UI设计相信大家在刚接触UI的时候都不太了解,我来说说我在一段学习时间后的了解. UI从工作内容上来说分为3大类,即研究工具,研究人与界面的关系,研究人与之相应. UI设计师的职能一个是图形设 ...