VSYNC即vertical sync,也称为垂直同步,是一种图形技术,主要就是强制将帧速率与显示器的刷新率同步,最初由 GPU 制造商提出,主要用来处理屏幕撕裂。首先了解下两个名词:FPS与屏幕刷新频率
帧率[Frame Rate,单位FPS]-显卡生成帧的速率,也可以认为是数据处理的速度
屏幕刷新频率 [Refresh Rate单位赫兹/HZ]:是指硬件设备刷新屏幕的频率,值一般是固定的,以黑白电视的电子扫描枪类比,比如60Hz的显示屏,每16ms电子枪从上到下从左到右一行一行逐渐把图片绘制出来。
两者要同步配合好才能高效的显示图像,可以人为帧率对应的是图像数据的输出,刷新率对应的是图像数据的屏幕展示,如果帧率同设备的刷新率不一致,而又没有采用合适的同步技术,会出现什么问题呢?可能会出现上述的屏幕撕裂[多帧的局部数据共同组成了一个完整帧],示意如下:

理论上来讲,只要没做到读/写线性同步就有几率发生撕裂, 只有帧数据完整更新+显示设备完整渲染才能阻止撕裂,相对应的撕裂的复现场景有两种:

所以同步锁的机制是撕裂的关键,必须有这么一个机制告诉GPU显卡,要等待当前帧绘完整,才能替换当前帧,即VSYNC,VSYNC强制帧率和显示器刷新频率同步,如果当前帧没绘制完,即使下一帧准备好了,也禁止使用下一帧,直到显示器绘制完当前帧,即:60HZ显示器,开了垂直同步后,显示帧率就会限定最高60,即使显卡输出高达90FPS也没用,甚至可以认为他是一种妥协性优化,一定程度上还会降低性能。以上都是针对一块显示存储的情况,理论上只要加锁就能解决,读的时候禁止写,但这么做无疑会大大降低效率,所以不能简单依靠单纯加锁解决问题。
如何解决单缓冲+同步的性能问题呢?多增加一块显示存储区能解决吗?假定显示设备有两块显存,BackBuffer与FrontBuffer,可以简单的认为CPU/GPU占据一个缓冲、当前呈现的数据占据一个缓冲,GPU/CPU 绘制更新BackBuffer,不需要关心正在展示的FrontBuffer,这就是双缓冲,相比于单缓存,双缓冲可让写与读分离,提高效率。但紧靠双缓冲理论上解决不了撕裂的问题,BackBuffer毕竟也是要展示的,也要”拷贝“到FrontBuffer,如果不对拷贝操作添加干预,也可能出现撕裂,VSYNC机制必须兼具禁止在刷新的过程中更新FrontBuffer的功能,所有的COPY或者说是Page flipping操作都要等待上一帧完全渲染完才可以,渲染完成之后,显示设备就按节奏可以发出下一个VSYNC信号,通知BackBuffer与FrontBuffer间进行拷贝,拷贝结束后,接着进行下一帧屏幕渲染,这样就能避免屏幕撕裂,当然,如果BackBuffer还未来得及完成帧更新也是需要阻断拷贝过程,否则就是渲染了半成品的帧,所以个人人为,Vsync解决撕裂、双缓冲来解决性能。
It does this by preventing the GPU from doing anything to the display memory until the monitor has concluded its current refresh cycle — effectively not feeding it any more information until it’s ready for it. Through a combination of double buffering and page flipping, VSync synchronizes the drawing of frames onto the display only when it has finished a refresh cycle, so you shouldn’t ever see tears when VSync is enabled.

对Android系统而言,VSYNC除了强制帧率和显示器刷新频率同步外,还有其他很多作用,在Android Jelly Bean之前VSYNC使用的场景比较少,只用在最后缓冲区切换,系统的其他环节没用,这种做法可能会让CPU浪费在其他低优先级的业务上,如下图:

如此情况就是一次jank

Jelly Bean之前VSYNC仅用在最后的图像显示阶段,防止屏幕撕裂,但是并未协调UI的绘制,CPU对于显示帧的处理是凌乱的,VSYNC到达后,如果CPU被其他任务占据,UI绘制的执行就会延迟,等到它开始处理UI生成帧的时候,可能已经处于16ms的中间,这样就很容易跨两个VYSNC信号,导致掉帧。在Jelly Bean中,下一帧的处理被限定在VSync信号到达时,并且依赖Android的消息屏障机制,将UI重绘消息的优先级是提高,其他的同步消息均不会执行,由于是在每个VSYNC信号到达时就处理帧,可以让UI绘制充分使用16ms耗时,可以尽量避免跨越两帧的情况出现。

这种做法保证了UI绘制的执行时间,虽然不能完全解决jank【比如本身绘制就超过16ms】,但是对于本来就小于16ms的任务是能保证的,从而降低jank的概率,因此VSYNC+双缓冲能够很好降低单缓冲的性能问题,降低延时。
之前的VSYNC+双缓冲流程图示都是用1、2、3代表第帧来表示更新流程,接线来用缓冲区代表,看一下双缓冲的数据流向,理想情况下,16ms内CPU处理完数据,将缓冲区A交给GPU,GPU接着处理A,结束后,等下个VSYNC与前面展示缓冲区B交换,A进行屏幕渲染,B回收用来继续生成下一帧,如下图所示:

在这种模型下,CPU与GPU其实是一种串行处理的操作,存在资源的浪费,因为两者其一必空闲,毕竟没有多余的缓冲区让其处理数据,理想情况下其实双缓冲并未有什么不妥,但是一旦CPU或者GPU处理超时,jank就很容易发生。
VSYNC+双缓冲保证低延时,三缓冲保证稳定性:让闲置的资源动起来
双缓冲模型中显示、CPU、GPU处理都会用到Buffer,VSYNC+双缓冲在理想情况下是没有问题的,但如果某个环节出现问题,那就不一样了,比如某些帧耗时是[CPU 8ms +GPU 12ms],超过了16ms,如下:

可以看到在第二个阶段,存在CPU资源浪费,双缓冲只会提供两个Buffer,B被GPU处理占用,A正在用显示,那么在第二个16ms里面,CPU就无法获取到Buffer处理UI更新,在Jank的阶段空空等待。而且,一般出现这种场景都是连续的:比如复杂视觉效果,那么GPU可能会一直超负荷,CPU一直跟GPU抢Buffer,这样带来的问题就是滚雪球似的掉帧,一直浪费,完全没有利用CPU与GPU并行处理的效率,成了串行处理,如下所示

如何处理呢?多增加一个Buffer给CPU用,让它提前忙起来,这样就能做到三方都有Buffer可用,CPU不用跟GPU争一个Buffer,真正实现并行处理。如下:

如上图所示,虽然即使每帧需要20ms【CPU 8ms +GPU 12ms】,但是由于多加了一个Buffer,实现了CPU跟GPU并行,便可以做到了只在开始掉一帧,后续却不掉帧,双缓冲充分利用16ms做到低延时,三缓冲保障了其稳定性,为什么4缓冲没必要呢?因为三个既可保证并行,四个徒增资源浪费。 在Android系统中,双缓冲不仅仅是两份存储,它是一个概念,双缓冲是一条链路,不是某一个环节,是整个系统采用的一个机制,需要各个环节的支持,从APP到SurfaceFlinger、到图像显示都要参与协作。对于APP端而言,每个Window都是一个双缓冲的模型,一个Window对应一个Surface,而每个Surface里至少映射两个存储区,一个给图层合成显示用,一个给APP端图形处理,这便是应于上层的双缓冲。
同步是防止画面撕裂的关键,VSYNC同步能防止画面撕裂
VSYNC+双缓冲在Android中能有序规划渲染流程,降低延时
Android已经采用了双缓冲,双缓冲不仅仅是两份存储,它是一个概念,双缓冲是一条链路,不是某一个环节,是整个系统采用的一个机制,需要各个环节的支持,从APP到SurfaceFlinger、到图像显示都要参与协作
三缓冲在UI复杂情况下能保证画面的连续性,提高柔韧性
作者:看书的小蜗牛
链接:https://juejin.cn/post/7125675253707046926
来源:稀土掘金 如有侵权,请联系删除!
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只
我正在使用active_admin,我在Rails3应用程序的应用程序中有一个目录管理,其中包含模型和页面的声明。时不时地我也有一个类,当那个类有一个常量时,就像这样:classFooBAR="bar"end然后,我在每个必须在我的Rails应用程序中重新加载一些代码的请求中收到此警告:/Users/pupeno/helloworld/app/admin/billing.rb:12:warning:alreadyinitializedconstantBAR知道发生了什么以及如何避免这些警告吗? 最佳答案 在纯Ruby中:classA