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(转)的更多相关文章

  1. WaitForTargetFPS

    WaitForTargetFPS,是关于帧数限制的,你可能开了垂直同步,其实是防止撕裂.先说撕裂,在显示器的帧缓存会被不同步的显卡的帧缓存给替换掉,导致显示器显示到一半的时候,内存被换掉,你看到上频是 ...

  2. 屏幕分辨率与FPS

    屏幕分辨率 刷新率分为垂直刷新率和水平刷新率,一般提到的刷新率通常指垂直刷新率. 垂直刷新率表示屏幕的图象每秒钟重绘多少次,也就是每秒钟屏幕刷新的次数,以Hz(赫兹)为单位. 刷新率越高越好,图象就越 ...

  3. UE4编程之C++创建一个FPS工程(二)角色网格、动画、HUD、子弹类

    转自:http://blog.csdn.net/u011707076/article/details/44243103 紧接上回,本篇文章将和大家一同整理总结UE4关于角色网格.动画.子弹类和HUD的 ...

  4. android应用程序fps meter[帧数显示]的分析 —— 浅谈root的风险 (3)

    上节已经详细说了下注入过程,最后寄生进程在宿主进程中下了个蛋,这下完的蛋有什么作用呢?接下来再具体分析一下. lib0的感染过程分析 对于本例注入的so动态库,首先看一下so的符号: $ readel ...

  5. "她等待刀尖已经太久"--茨维塔耶娃诗抄

      生活   1 你无法夺走我的红晕—— 它强大——如同河水的汛潮! 你是猎人,可我不会上当, 你若追逐,我就会逃跑.   你无法夺走我鲜活的灵魂! 就这样,在急遽的追逐中—— 一匹阿拉伯的骏马, 微 ...

  6. FPS游戏服务器设计的问题 【转】

    一.追溯 去gameloft笔试,有一个题目是说: 叫你去设计一个FPS(第一人称射击游戏),你是要用TCP呢还是要用UDP,说明理由 . 二.学习 这是两篇网上找到的文章,写非常不错. 当时笔试的时 ...

  7. (转)FPS游戏服务器设计的问题

    FPS游戏服务器设计的问题出处:http://www.byteedu.com/thread-20-1-1.html一.追溯 去gameloft笔试,有一个题目是说: 叫你去设计一个FPS(第一人称射击 ...

  8. 4、APP FPS测试

    什么是FPS FPS是图像领域中的定义,是指画面每秒传输帧数,通俗来讲就是指动画或视频的画面数.FPS是测量用于保存.显示动态视频的信息数量.每秒钟帧数愈多,所显示的动作就会愈流畅.通常,要避免动作不 ...

  9. java 线程 Lock 锁使用Condition实现线程的等待(await)与通知(signal)

    一.Condition 类 在前面我们学习与synchronized锁配合的线程等待(Object.wait)与线程通知(Object.notify),那么对于JDK1.5 的 java.util.c ...

随机推荐

  1. KEGG数据库介绍

    转载自https://mp.weixin.qq.com/s/pqbMXMkuqEXbLf31PTxGZQ KEGG简介 KEGG 数据库于 1995 年由 Kanehisa Laboratories ...

  2. Spring Cloud feign

    Spring Cloud feign使用 前言 环境准备 应用模块 应用程序 应用启动 feign特性 综上 1. 前言 我们在前一篇文章中讲了一些我使用过的一些http的框架 服务间通信之Http框 ...

  3. 第六章 图(c)广度优先搜索

  4. 第七章 二叉搜索树 (d3)AVL树:删除

  5. 获取客户端真实IP地址

    Java-Web获取客户端真实IP: 发生的场景:服务器端接收客户端请求的时候,一般需要进行签名验证,客户端IP限定等情况,在进行客户端IP限定的时候,需要首先获取该真实的IP. 一般分为两种情况: ...

  6. OC - runtime - 1

  7. IIS支持IPA、APK文件的下载

    IIS里MIME类型中默认是没有ipa,apk文件的,所以无法直接通过网络下载.   解决方法如下: 1.打开IIS信息服务管理器,选中自已的网站,在右边面板中找到MIME类型. 2.双击打开,点击右 ...

  8. spring框架的概述与入门

    1. Spring框架的概述 * Spring是一个开源框架 * Spring是于2003 年兴起的一个轻量级的Java开发框架,由Rod Johnson在其著作Expert One-On-One J ...

  9. Halcon的C#二次开发及经验分享

    本文涉及面较广,因此很难在所有方面都讲解得很详细,故适合具有一定Halcon开发经验的人阅读. 1.Halcon二次开发的两种方式 ① 使用C#的语法方式逐句改写Halcon代码 优点:各种变量的类型 ...

  10. DOMNodeInserted监听div内容改变

    $('.cw-icon .ci-count').on('DOMNodeInserted',function(){ $(".settleup-content .cont_loading&quo ...