Open GL 渲染存在的問(wèn)題和解決方案-2

屏幕卡頓

屏幕卡頓是指圖形圖像的在顯示時(shí)出現(xiàn)了撕裂(即圖片錯(cuò)位顯示)、掉幀(重復(fù)顯示同一幀數(shù)據(jù))等問(wèn)題空闲,導(dǎo)致用戶能直觀的從屏幕上看到的一種異沉铊荆現(xiàn)象
為什么會(huì)出現(xiàn)這種情況呢?下面就來(lái)詳細(xì)解說(shuō)下屏幕卡頓

屏幕卡頓原因

主要以下三種原因

  • CPU和GPU在渲染的流水線中耗時(shí)過(guò)長(zhǎng)碴倾,導(dǎo)致從緩存區(qū)獲取位圖顯示時(shí)逗噩,下一幀的數(shù)據(jù)還沒(méi)有準(zhǔn)備好,獲取的仍是上一幀的數(shù)據(jù)跌榔,產(chǎn)生掉幀現(xiàn)象异雁,掉幀就會(huì)導(dǎo)致屏幕卡頓
  • 蘋(píng)果官方針對(duì)屏幕撕裂問(wèn)題,目前一直使用的方案是垂直同步+雙緩存區(qū)僧须,可以從根本上防止和解決屏幕撕裂纲刀,但是同時(shí)也導(dǎo)致了新的問(wèn)題掉幀。雖然我們采用了雙緩存區(qū)担平,但是我們并不能解決CPU和GPU處理圖形圖像的速度問(wèn)題示绊,導(dǎo)致屏幕在接收到垂直信號(hào)時(shí),數(shù)據(jù)尚未準(zhǔn)備好暂论,緩存區(qū)仍是上一幀的數(shù)據(jù)面褐,因此導(dǎo)致掉幀
  • 在垂直同步+雙緩存區(qū)的方案上,再次進(jìn)行優(yōu)化取胎,將雙緩存區(qū)展哭,改為三緩存區(qū),這樣其實(shí)也并不能從根本上解決掉幀的問(wèn)題闻蛀,只是比雙緩存區(qū)掉幀的概率小了很多匪傍,仍有掉幀的可能性,對(duì)于用戶而言觉痛,可能是無(wú)感知的役衡。
    詳解解釋一下屏幕撕裂問(wèn)題
    我們看一下下面這張圖是典型的撕裂問(wèn)題造成的
image.png

在講屏幕撕裂之前,首先說(shuō)說(shuō)屏幕是如何成像的秧饮,主要的流程是什么
屏幕成像過(guò)程
請(qǐng)看下面這張圖映挂,詳細(xì)說(shuō)明了屏幕成像的一個(gè)流程

image.png

  • 將需要顯示的圖像泽篮,經(jīng)由GPU渲染
  • 將渲染后的結(jié)果,存儲(chǔ)到幀緩存區(qū)柑船,幀緩存區(qū)中存儲(chǔ)的格式是位圖
  • 由視頻控制器從幀緩存區(qū)中讀取位圖帽撑,交由顯示器,從左上角逐行掃描進(jìn)行顯示
    屏幕撕裂原因
  • 在屏幕顯示圖形圖像的過(guò)程中鞍时,是不斷從幀緩存區(qū)獲取一幀一幀數(shù)據(jù)進(jìn)行顯示的亏拉,然后在渲染的過(guò)程中,幀緩存區(qū)中仍是舊的數(shù)據(jù)逆巍,屏幕拿到舊的數(shù)據(jù)去進(jìn)行顯示及塘,在舊的數(shù)據(jù)沒(méi)有讀取完時(shí) ,新的一幀數(shù)據(jù)處理好了锐极,放入了緩存區(qū)笙僚,這時(shí)就會(huì)導(dǎo)致屏幕另一部分的顯示是獲取的新數(shù)據(jù),從而導(dǎo)致屏幕上呈現(xiàn)圖片不匹配灵再,人物肋层、景象等錯(cuò)位顯示的情況。
    本人一開(kāi)始對(duì)幀的概念不是很理解翎迁,下面對(duì)幀和幀緩存做一下解釋
    • 在放映電影的過(guò)程中栋猖,畫(huà)面被一幅幅地放映在銀幕上。畫(huà)幅移開(kāi)時(shí)汪榔,光線就被遮住蒲拉,幕上便出現(xiàn)短暫的黑暗;每放映一個(gè)畫(huà)幅后痴腌,幕上就黑暗一次雌团。但這一次次極短暫的黑暗,被人的視覺(jué)生理現(xiàn)象“視覺(jué)暫留”所彌補(bǔ)衷掷。人眼在觀察景物時(shí)辱姨,光信號(hào)傳入大腦神經(jīng)需經(jīng)過(guò)一段短暫時(shí)間,光的作用結(jié)束時(shí)戚嗅,視覺(jué)也不立即消失。視覺(jué)的這一現(xiàn)象稱為“視覺(jué)暫留”枢舶。當(dāng)電影畫(huà)面換幅頻率達(dá)到每秒15幅~30幅時(shí)懦胞,觀眾便見(jiàn)不到黑暗的間隔了,這時(shí)人“看到”的就是運(yùn)動(dòng)的事物凉泄,這就是電影的基本原理躏尉。這里的一幅畫(huà)面就是電影的一幀,實(shí)際上就是電影膠片中的一格后众。

    • 幀——就是影像動(dòng)畫(huà)中最小單位的單幅影像畫(huà)面胀糜。 一幀就是一副靜止的畫(huà)面颅拦,連續(xù)的幀就形成動(dòng)畫(huà),如電視圖象等教藻。 通常說(shuō)幀數(shù)距帅,簡(jiǎn)單地說(shuō),就是在1秒鐘時(shí)間里傳輸?shù)膱D片的幀數(shù)括堤,也可以理解為圖形處理器每秒鐘能夠刷新幾次碌秸,通常用FPS(Frames Per Second)表示。每一幀都是靜止的圖象悄窃,快速連續(xù)地顯示幀便形成了運(yùn)動(dòng)的假象讥电。高的幀率可以得到更流暢、更逼真的動(dòng)畫(huà)轧抗。每秒鐘幀數(shù) (fps) 越多恩敌,所顯示的動(dòng)作就會(huì)越流暢。

    • 幀緩沖存儲(chǔ)器(Frame Buffer):簡(jiǎn)稱幀緩存或顯存横媚,它是屏幕所顯示畫(huà)面的一個(gè)直接映象潮剪,又稱為位映射圖(Bit Map)或光柵。幀緩存的每一存儲(chǔ)單元對(duì)應(yīng)屏幕上的一個(gè)像素分唾,整個(gè)幀緩存對(duì)應(yīng)一幀圖像抗碰。

      image.png

蘋(píng)果官方解決撕裂問(wèn)題方案

蘋(píng)果官方針對(duì)屏幕撕裂現(xiàn)象,目前一直采用的是垂直同步+雙緩存绽乔,該方案是強(qiáng)制要求同步弧蝇,且是以掉幀為代價(jià)的。

以下是垂直同步+雙緩存的一個(gè)圖解過(guò)程


image.png
  • 垂直同步:是指給幀緩沖加鎖折砸,當(dāng)電子光束掃描的過(guò)程中看疗,只有掃描完成了才會(huì)讀取下一幀的數(shù)據(jù),而不是只讀取一部分
  • 雙緩沖區(qū):采用兩個(gè)幀緩沖區(qū)用途GPU處理結(jié)果的存儲(chǔ)睦授,當(dāng)屏幕顯示其中一個(gè)緩存區(qū)內(nèi)容時(shí)两芳,另一個(gè)緩沖區(qū)繼續(xù)等待下一個(gè)緩沖結(jié)果,兩個(gè)緩沖區(qū)依次進(jìn)行交替

掉幀

采用蘋(píng)果的雙緩沖區(qū)方案后去枷,又會(huì)出現(xiàn)新的問(wèn)題怖辆,掉幀。
什么是掉幀删顶?簡(jiǎn)單來(lái)說(shuō)就是 屏幕重復(fù)顯示同一幀數(shù)據(jù)的情況就是掉幀

如圖所示:當(dāng)前屏幕顯示的是A竖螃,在收到垂直信號(hào)后,CPU和GPU處理的B還沒(méi)有準(zhǔn)備好逗余,此時(shí)特咆,屏幕顯示的仍然是A


image.png
  • 針對(duì)掉幀情況,我們可以在蘋(píng)果方案的基礎(chǔ)上進(jìn)行優(yōu)化录粱,即采用三緩存區(qū)腻格,意味著画拾,在屏幕顯示時(shí),后面還準(zhǔn)備了3個(gè)數(shù)據(jù)用于顯示菜职,但是這只能減少掉幀情況仍然無(wú)法從根本上解決掉幀問(wèn)題青抛。

iOS 中渲染流程

在iOS中渲染的整體流程如下所示

image.png
  • App通過(guò)調(diào)用CoreGraphics、CoreAnimation些楣、CoreImage等框架的接口觸發(fā)圖形渲染操作脂凶,CoreGraphics、CoreAnimation愁茁、CoreImage等框架將渲染交由OpenGL ES蚕钦,OpenGL ES來(lái)驅(qū)動(dòng)GPU做渲染,最后顯示到屏幕上鹅很,由于OpenGL ES 是跨平臺(tái)的嘶居,所以在他的實(shí)現(xiàn)中,是不能有任何窗口相關(guān)的代碼促煮,而是讓各自的平臺(tái)為OpenGL ES提供載體邮屁。在ios中,如果需要使用OpenGL ES菠齿,就是通過(guò)CoreAnimation提供窗口佑吝,讓App可以去調(diào)用。

iOS中渲染框架總結(jié)

主要由以下六種框架


image.png

View 與 CALayer 的關(guān)系

首先分別簡(jiǎn)單說(shuō)下UIView和CALayer各自的作用

  • UIView

    • UIView屬于UIKIt
    • 負(fù)責(zé)繪制圖形和動(dòng)畫(huà)操作
    • 用于界面布局和子視圖的管理
    • 處理用戶的點(diǎn)擊事件
  • CALayer

    • CALayer屬于CoreAnimation
    • 只負(fù)責(zé)顯示绳匀,且顯示的是位圖
    • CALayer既用于UIKit芋忿,也用于APPKit,
      ==> UIKit是iOS平臺(tái)的渲染框架疾棵,APPKit是Mac OSX系統(tǒng)下的渲染框架戈钢,
      ==> 由于iOS和Mac兩個(gè)系統(tǒng)的界面布局并不是一致的,iOS是基于多點(diǎn)觸控的交互方式是尔,而Mac OSX是基于鼠標(biāo)鍵盤(pán)的交互方式殉了,且分別在對(duì)應(yīng)的框架中做了布局的操作,所以并不需要layer載體去布局拟枚,且不用迎合任何布局方式薪铜。

UIView和CALayer的關(guān)系

  • UIView基于UIKit框架,可以處理用戶觸摸事件梨州,并管理子視圖
  • CALayer基于CoreAnimation痕囱,而CoreAnimation是基于QuartzCode的。所以CALayer只負(fù)責(zé)顯示暴匠,不能處理用戶的觸摸事件
  • 從父類來(lái)說(shuō),CALayer繼承的是NSObject傻粘,而UIView是直接繼承自UIResponder的每窖,所以UIVIew相比CALayer而言帮掉,只是多了事件處理功能,
  • 從底層來(lái)說(shuō)窒典,UIView屬于UIKit的組件蟆炊,而UIKit的組件到最后都會(huì)被分解成layer,存儲(chǔ)到圖層樹(shù)中
  • 在應(yīng)用層面來(lái)說(shuō)瀑志,需要與用戶交互時(shí)涩搓,使用UIView,不需要交互時(shí)劈猪,使用兩者都可以

UIView和CALayer的渲染

下圖可以說(shuō)明view 和 layer之間是如何渲染的


image.png

界面觸發(fā)的方式有兩種

  • ==> 通過(guò)loadView中子View的drawRect方法觸發(fā):會(huì)回調(diào)CoreAnimation中監(jiān)聽(tīng)Runloop的BeforeWaiting的RunloopObserver昧甘,通過(guò)RunloopObserver來(lái)進(jìn)一步調(diào)用CoreAnimation內(nèi)部的CA::Transaction::commit(),進(jìn)而一步步走到drawRect方法
    ==> 用戶點(diǎn)擊事件觸發(fā):?jiǎn)拘裄unloop战得,由source1處理(__IOHIDEventSystemClientQueueCallback)充边,并且在下一個(gè)runloop里由source0轉(zhuǎn)發(fā)給UIApplication(_UIApplicationHandleEventQueue),從而能通過(guò)source0里的事件隊(duì)列來(lái)調(diào)用CoreAnimation內(nèi)部的CA::Transaction::commit();方法常侦,進(jìn)而一步一步的調(diào)用drawRect浇冰。
    最終都會(huì)走到CoreAnimation中的CA::Transaction::commit()方法,從而來(lái)觸發(fā)UIView和CALayer的渲染,這時(shí)聋亡,已經(jīng)到了CoreAnimation的內(nèi)部肘习,即調(diào)用CA::Transaction::commit();來(lái)創(chuàng)建CATrasaction,然后進(jìn)一步調(diào)用 CALayer drawInContext:()回調(diào)CALayer的Delegate(UIView)坡倔,問(wèn)UIView沒(méi)有需要畫(huà)的內(nèi)容漂佩,即回調(diào)到drawRect:方法在drawRect:方法里可以通過(guò)CoreGraphics函數(shù)或UIKit中對(duì)CoreGraphics封裝的方法進(jìn)行畫(huà)圖操作將繪制好的位圖交由CALayer,由OpenGL ES 傳送到GPU的幀緩沖區(qū),等屏幕接收到垂直信號(hào)后致讥,就讀取幀緩沖區(qū)的數(shù)據(jù)仅仆,顯示到屏幕上

CoreAnimation

在蘋(píng)果官方的描述中,Render垢袱、Compose墓拜,and animate visual elements,CoreAnimationg中的動(dòng)畫(huà)只是一部分请契,它其實(shí)是一個(gè)復(fù)合引擎咳榜,主要的職責(zé)包括 渲染、構(gòu)建和動(dòng)畫(huà)實(shí)現(xiàn)爽锥。

ios中CoreAnimation如圖所示


image.png
  • ios中基于CoreAnimation構(gòu)建的框架有兩個(gè):UIKit和APPKit
  • CoreAnimation 又是基于Metal 涌韩、CoreGraphics封裝的
    蘋(píng)果為什么要基于UIView和CALayer提供兩個(gè)平行的層級(jí)關(guān)系(UIKit 和APPKit)?
    • 職責(zé)分離氯夷,可以避免大量重復(fù)代碼
    • 兩個(gè)系統(tǒng)交互規(guī)則不一致臣樱,雖然功能上類似,但實(shí)現(xiàn)上有顯著區(qū)別

CoreAnimation中的渲染流水線

CoreAnimation中渲染的流程如圖所示


image.png

主要分為兩部分:

  • CoreAnimation部分
  • GPU部分
    CoreAnimation部分
  • App處理UIView、UIButton等載體的事件雇毫,然后通過(guò)CPU完成對(duì)顯示內(nèi)容的計(jì)算玄捕,并將計(jì)算后的圖層進(jìn)行打包,在下一次runloop時(shí)棚放,發(fā)送到渲染服務(wù)器
  • Render Server中主要對(duì)收到的準(zhǔn)備顯示的內(nèi)容進(jìn)行解碼枚粘,然后執(zhí)行OpenGL等相關(guān)程序,并調(diào)用GPU進(jìn)行渲染
    ==> Render Server 操作分析
    image.png

    GPU部分
  • GPU中通過(guò)頂點(diǎn)著色器飘蚯、片元著色器完成對(duì)顯示內(nèi)容的渲染馍迄,將結(jié)果存入幀緩存區(qū)
  • GPU通過(guò)幀緩存區(qū)、視頻控制器等相關(guān)部件局骤,將其顯示到屏幕上

總結(jié)

屏幕撕裂問(wèn)題和掉幀問(wèn)題根本的原因是CPU和GPU的計(jì)算能力跟不上幀的刷新率(60FPS)才會(huì)有可能發(fā)生撕裂或者掉幀攀圈,在iOS的設(shè)備上經(jīng)過(guò)蘋(píng)果的垂直同步信號(hào)+雙緩沖區(qū)的解決方案后很少看到撕裂,除非是在舊的設(shè)備(例如iphone4s)或運(yùn)行對(duì)畫(huà)質(zhì)要求特別高的游戲或復(fù)雜動(dòng)畫(huà)有可能會(huì)出現(xiàn)撕裂庄涡,掉幀也是同樣的道理量承。iOS 繪制普通的UIView、UIImageView等是不會(huì)出現(xiàn)撕裂或掉幀的穴店。雖然蘋(píng)果的解決方案沒(méi)有從根本上解決存在的問(wèn)題撕捍,但是我們的實(shí)際體驗(yàn)中撕裂和掉幀問(wèn)題并不常見(jiàn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末泣洞,一起剝皮案震驚了整個(gè)濱河市忧风,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌球凰,老刑警劉巖狮腿,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異呕诉,居然都是意外死亡缘厢,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)甩挫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)贴硫,“玉大人,你說(shuō)我怎么就攤上這事伊者∮⒃猓” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵亦渗,是天一觀的道長(zhǎng)挖诸。 經(jīng)常有香客問(wèn)我,道長(zhǎng)法精,這世上最難降的妖魔是什么多律? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任痴突,我火速辦了婚禮,結(jié)果婚禮上菱涤,老公的妹妹穿的比我還像新娘苞也。我一直安慰自己洛勉,他們只是感情好粘秆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著收毫,像睡著了一般攻走。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上此再,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天昔搂,我揣著相機(jī)與錄音,去河邊找鬼输拇。 笑死摘符,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的策吠。 我是一名探鬼主播逛裤,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼猴抹!你這毒婦竟也來(lái)了带族?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蟀给,失蹤者是張志新(化名)和其女友劉穎蝙砌,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體跋理,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡择克,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了前普。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肚邢。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖汁政,靈堂內(nèi)的尸體忽然破棺而出道偷,到底是詐尸還是另有隱情,我是刑警寧澤记劈,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布勺鸦,位于F島的核電站,受9級(jí)特大地震影響目木,放射性物質(zhì)發(fā)生泄漏换途。R本人自食惡果不足惜懊渡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望军拟。 院中可真熱鬧剃执,春花似錦、人聲如沸懈息。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)辫继。三九已至怒见,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間姑宽,已是汗流浹背遣耍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炮车,地道東北人舵变。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瘦穆,于是被迫代替她去往敵國(guó)和親纪隙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345