jjzjj

Android三重缓冲-预期行为?

coder 2023-11-22 原文

我正在研究应用程序的性能,因为我注意到它在滚动时会掉落一些帧。我运行了systrace(在运行4.3的Nexus 4上),并在输出中注意到了interesting section

起初一切都很好。 Zooming in on the left section,我们可以看到绘图在每个vsync上开始,以剩余的时间结束,并等到下一个vsync为止。由于它是三重缓冲的,因此应将其绘制到一个缓冲区中,然后在完成后将其发布到以下vsync上。

在放大的屏幕快照中的第4个vsync上,该应用程序执行了一些工作,并且下一个vsync的绘制操作无法及时完成。但是,我们不会丢弃任何帧,因为先前的抽奖正在前面进行。

但是,在这种情况发生之后,绘制操作不会弥补丢失的vsync。相反,每个vsync仅启动一个绘制操作,现在它们不再向前绘制一帧。

Zooming in on the right section,应用程序完成了更多工作,并且错过了另一个vsync。由于我们没有在前面画一个框架,因此实际上在这里放置了一个框架。此后,它可以回到前面绘制一帧。

这是预期的行为吗?我的理解是,如果您错过了vsync,则三重缓冲可以使您恢复,但是这种行为看起来像是您错过的每两个vsync都会丢掉一帧。

跟进问题

  • this screenshot的右侧,应用程序渲染缓冲区的速度实际上比显示器消耗缓冲区的速度快。假设在PerformTraversals#1(在屏幕快照中标记)期间,正在显示缓冲区A,并且正在渲染缓冲区B。 #1在vsync之前完成很长时间,并将缓冲区B放入队列中。此时,应用程序是否应该能够立即开始渲染缓冲区C?相反,performTraversals#2直到下一个vsync才开始,这浪费了之间的宝贵时间。
  • 同样,对于waitForever on the left side here的需求我也有些困惑。假设正在显示缓冲区A,正在队列B中,正在渲染缓冲区C。缓冲区C完成渲染后,为什么不立即将其添加到队列中?相反,它会执行waitForever直到从队列中删除缓冲区B,然后才添加缓冲区C,这就是为什么无论应用程序渲染缓冲区的速度如何,队列似乎始终保持在大小1。
  • 最佳答案

    仅当您保持缓冲区已满时,才提供缓冲的数量。这意味着渲染速度快于显示器消耗它们的速度。

    标签没有出现在图像中,但是我猜测绿色vsync行上方的紫色行是BufferQueue状态。您可以看到它通常随时都有0或1个完整的缓冲区。在“左侧放大”图像的最左侧,您可以看到它有两个缓冲区,但是之后只有一个,在屏幕的3/4处,您会看到一个很短的紫色条,表示它几乎没有及时渲染帧。

    有关背景,请参见this postthis post

    更新以解决所添加的问题...

    the other post中的细节几乎没有爬取表面。我们必须更深入。

    systrace中显示的BufferQueue计数是排队的缓冲区的数量,即其中包含内容的缓冲区的数量。当SurfaceFlinger抓取要显示的缓冲区时,它将立即释放该缓冲区,并将其状态更改为“空闲”。当缓冲区显示在叠加层上时,这特别令人兴奋,因为显示是直接从缓冲区渲染的(与合成暂存缓冲区并显示相反)。

    我再说一遍:显示器正在主动从中读取数据以在屏幕上显示的缓冲区在BufferQueue中被标记为“空闲”。缓冲区具有关联的围栅,该围栅最初是“Activity 的”。处于 Activity 状态时,不允许任何人修改缓冲区内容。当显示器不再需要缓冲区时,它将向篱笆发出信号。

    因此,跟踪左侧的代码位于waitForever()中的原因是因为它正在等待栅栏发出信号。当VSYNC命中时,显示切换到另一个缓冲区,向围墙发出信号,您的应用程序可以立即开始使用该缓冲区。这样可以消除因必须等待SurfaceFlinger唤醒,不再使用缓冲区,通过BufferQueue发送IPC释放缓冲区等而导致的等待时间。

    请注意,仅当您不落后时(对跟踪的左侧和右侧),才会显示对waitForever()的调用。当队列只有1个完整的缓冲区时,我不确定为什么会发生这种情况-应该使最早的缓冲区出队,该缓冲区应该已经发出信号了。

    最重要的是,您将永远不会看到BufferQueue在三重缓冲之上超过两个。

    并非所有设备都如上所述工作。 Nexus 7(2012)并未使用“显式同步”机制,并且ICS之前的设备完全没有BufferQueues。

    回到编号的屏幕截图,是的,在“1”和“2”之间有足够的时间可以让您的应用运行performTraversals()。很难肯定地说不知道您的应用程序在做什么,但是我想您已经有了一个由Choreographer驱动的动画周期,该周期会唤醒每个VSYNC并起作用。它没有比这更频繁地运行。

    如果您使用systrace Android Breakout,则可以尽可能快地进行渲染(“队列填充”),并依靠BufferQueue背压来调节游戏速度,从而看到它的外观。

    比较运行4.3的N4和运行4.4的N4尤其有趣。在4.3上,跟踪与您相似,队列在1处徘徊,规则降为0,在2处偶尔出现尖峰。在4.4上,队列几乎总是在2处,偶尔下降到1。睡在eglSwapBuffers();在4.3中,跟踪通常显示在其下方的waitForever(),而在4.4中,其显示dequeueBuffer()。 (我不知道产生这种意外原因的原因。)

    更新2: 4.3和4.4之间存在差异的原因似乎是Nexus 4驱动程序的更改。 4.3驱动程序使用了旧的dequeueBuffer调用,该调用变成了dequeueBuffer_DEPRECATED()(Surface.cpp line 112)。旧的接口(interface)不会将fence作为“输出”参数,因此调用必须调用waitForever()本身。较新的接口(interface)只是将篱笆返回给GL驱动程序,GL驱动程序会在需要时进行等待(可能不会立即执行)。

    更新3: here现在可以使用甚至更长的解释。

    关于Android三重缓冲-预期行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23377564/

    有关Android三重缓冲-预期行为?的更多相关文章

    1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

      我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

    2. ruby-on-rails - ruby 日期方程不返回预期的真值 - 2

      为什么以下不同?Time.now.end_of_day==Time.now.end_of_day-0.days#falseTime.now.end_of_day.to_s==Time.now.end_of_day-0.days.to_s#true 最佳答案 因为纳秒数不同:ruby-1.9.2-p180:014>(Time.now.end_of_day-0.days).nsec=>999999000ruby-1.9.2-p180:015>Time.now.end_of_day.nsec=>999999998

    3. 安卓apk修改(Android反编译apk) - 2

      最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

    4. ruby - Ruby gsub 替换中的行为不一致? - 2

      两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio

    5. ruby-on-rails - Ruby 中意外的大小写行为 - 2

      我在一段非常简单的代码(如我所想)中得到了一个错误的值:org=4caseorgwhenorg=4val='H'endputsval=>nil请不要生气,我希望我错过了一些非常明显的东西,但我真的想不通。谢谢。 最佳答案 这是典型的Ruby错误。case有两种被调用的方法,一种是你传递一个东西作为分支的基础,另一种是你不传递的东西。如果您确实在case中指定了一个表达式语句然后评估所有其他条件并与===进行比较.在这种情况下org评估为false和org===false显然不是真的。所有其他情况也是如此,它们要么是真的,要么是假的。

    6. ruby - 使对象的行为类似于 ruby​​ 中并行分配的数组 - 2

      假设您在Ruby中执行此操作:ar=[1,2]x,y=ar然后,x==1和y==2。是否有一种方法可以在我自己的类中定义,从而产生相同的效果?例如rb=AllYourCode.newx,y=rb到目前为止,对于这样的赋值,我所能做的就是使x==rb和y=nil。Python有这样一个特性:>>>classFoo:...def__iter__(self):...returniter([1,2])...>>>x,y=Foo()>>>x1>>>y2 最佳答案 是的。定义#to_ary。这将使您的对象被视为要分配的数组。irb>o=Obje

    7. ruby - 了解在 Ruby 中与 lambda 一起使用的 inject 行为 - 2

      我经常将预配置的lambda插入可枚举的方法中,例如“map”、“select”等。但是“注入(inject)”的行为似乎有所不同。例如与mult4=lambda{|item|item*4}然后(5..10).map&mult4给我[20,24,28,32,36,40]但是,如果我制作一个2参数lambda用于像这样的注入(inject),multL=lambda{|product,n|product*n}我想说(5..10).inject(2)&multL因为“inject”有一个可选的单个初始值参数,但这给了我......irb(main):027:0>(5..10).inject

    8. ruby - Sinatra session 未按预期持续 - 2

      我正在尝试使用Sinatra中的重定向和session在网站周围传递一些数据。这是一个简化的示例,使用PrettyPrint进行调试:require'pp'require'rubygems'require'sinatra'enable:sessionsget'/'dosession[:foo]='12345'puts'session1'ppsessionredirectto('/redir')endget'/redir'doputs'session2'ppsession'helloworld'end查看Thin的输出,我看到:>>Listeningon0.0.0.0:4567,CTRL

    9. ruby - 为什么 Minitest 的 assert_raises 在这种情况下没有按预期工作? - 2

      我正在尝试使用ActionControllerbugreporttemplate解决Rails中的一个奇怪行为.为了记录,这是模板中的Controller:classTestController我已经为缺失的Action添加了一条路线:routes.drawdoget'/'=>'test#index'get'/missing'=>'test#missing'end并且我试图断言AbstractController::ActionNotFound在遵循该路线时被引发:classBugTest预期行为:绿色测试。实际行为:#Runningtests:D,[2014-04-24T09:17:

    10. ruby - 奇怪的 ruby​​ for 循环行为(为什么这样做有效) - 2

      defreverse(ary)result=[]forresult[0,0]inaryendresultendassert_equal["baz","bar","foo"],reverse(["foo","bar","baz"])这行得通,我想了解原因。有什么解释吗? 最佳答案 如果我使用each而不是for/in重写它,它看起来像这样:defreverse(ary)result=[]#forresult[0,0]inaryary.eachdo|item|result[0,0]=itemendresultendforainb基本上就

    随机推荐