1. 概述
寫這篇總結(jié)的目的是想讓大家了解眼睛看到屏幕上漂亮的顏色和圖片背后所發(fā)生的事情。其實(shí)題目取的有點(diǎn)夸張,因?yàn)檫@里涉及到的知識(shí)非常多,所以這篇文章會(huì)我挑一些重要的內(nèi)容來講。
2. 什么是像素
簡(jiǎn)單的講像素就是組成數(shù)字圖像的基本單元。我們看到的圖像其實(shí)都是由一個(gè)個(gè)像素組成的尸曼。每個(gè)像素可有各自的顏色值,像素可分成紅萄焦、綠控轿、藍(lán)三原色像素(RGB色域)冤竹,或者青、品紅茬射、黃和黑(CMYK色域鹦蠕,印刷行業(yè)以及打印機(jī)中常見)。我們手機(jī)在抛、電腦钟病、以及電視等屏幕上的像素是由紅,綠刚梭,藍(lán)三種顏色組件構(gòu)成的肠阱。單位面積內(nèi)的像素越多代表分辨率越高。
800萬(wàn)像素 3,264 × 2,448=7,990,272
1000萬(wàn)像素 3648 × 2,736=9,980,928
3. 軟件組成
GPU(圖像處理單元)一般都用來處理圖像朴读。他具有很高的并發(fā)性屹徘,這也是為什么它能同時(shí)更新所有的像素,并呈現(xiàn)到顯示器上衅金。
3.1 GPU 與 CPU 的比較
理解 GPU 和 CPU 之間區(qū)別的一種簡(jiǎn)單方式是比較它們?nèi)绾翁幚砣蝿?wù)缘回。本質(zhì)上 CPU 由專為順序串行處理而優(yōu)化的幾個(gè)核心組成,而 GPU 則擁有一個(gè)由數(shù)以千計(jì)的更小典挑、更高效的核心(專為同時(shí)處理多重任務(wù)而設(shè)計(jì))組成的大規(guī)模并行計(jì)算架構(gòu)。
GPU 具有非常出色的浮點(diǎn)計(jì)算性能啦吧,工作效率上比 CPU 更省電您觉。
CPU 可以做很多不同的事情,但是合成圖像上卻比不過 GPU授滓。
另外還有可能大家都不知道的是:現(xiàn)在熱門的機(jī)器學(xué)習(xí)琳水,深度學(xué)習(xí)都用是 GPU,各大平臺(tái)也在推出 GPU 云計(jì)算服務(wù)般堆。
3.2 那么為什么是 GPU 呢在孝?
主要原因是:深度學(xué)習(xí)需要很高的內(nèi)在并行度、大量的浮點(diǎn)計(jì)算能力以及矩陣預(yù)算淮摔,深度學(xué)習(xí)的工作 99% 都可以類比成將不同的矩陣進(jìn)行相乘或者矩陣和向量進(jìn)行相乘私沮。剛好 GPU 可以提供這些能力,并且在相同的精度下和橙,相對(duì)傳統(tǒng) CPU 的方式仔燕,擁有更快的處理速度、更少的服務(wù)器投入和更低的功耗魔招。
http://www.nvidia.cn/object/machine-learning-cn.html
3.3 OpenGL
OpenGL(Open Graphics Library) 晰搀,它是在1992年第一次發(fā)布的(20多年前的事了)第一個(gè)和圖形硬件(GPU)交流的標(biāo)準(zhǔn)化方式,簡(jiǎn)單的說就是提供圖像 2D 和 3D 圖形渲染的 API 的底層圖形庫(kù)办斑。他雖然非常底層外恕,但是當(dāng)這是一個(gè)重大的飛躍,程序員不再需要為每個(gè)GPU重寫他們的驅(qū)動(dòng)應(yīng)用了。
OpenGL 之上擴(kuò)展出很多東西鳞疲。(看上面的架構(gòu)圖)在 iOS 上罪郊,幾乎所有的東西都是通過 Core Animation 繪制出來,Core Animation 是基于Core Graphics 和 Core Animation 的更上層的抽象建丧。
對(duì)于一些專門的應(yīng)用排龄,尤其是游戲,可能直接和 OpenGL/OpenGL ES 交流翎朱。
這些框架都是運(yùn)行在 GPU 上的橄维,所以要記住 GPU 是一個(gè)非常強(qiáng)大的圖形硬件,并且在顯示像素方面起著核心作用拴曲。
**4. 參與的硬件争舞,闡述需要遇到的挑戰(zhàn) **
第一個(gè)挑戰(zhàn)正如上面這張簡(jiǎn)單的圖片:GPU 需要將每一個(gè) frame 的圖層(位圖:可以直接理解成一個(gè)畫面)合成在一起(一秒60次),每一個(gè)圖層會(huì)占用 VRAM(video RAM)澈灼,GPU 在合成方面非常高效竞川,但是某些合成任務(wù)卻比其他更復(fù)雜,并且 GPU在 16.7ms(1/60s)內(nèi)能做的工作也是有限的叁熔。
下一個(gè)挑戰(zhàn)就是將數(shù)據(jù)傳輸?shù)?GPU 上委乌。為了讓 GPU 訪問數(shù)據(jù),需要將數(shù)據(jù)從 RAM 移動(dòng)到 VRAM 上荣回。這就是提及到的上傳數(shù)據(jù)到 GPU遭贸。這看起來貌似微不足道,但是一些大型的圖層卻會(huì)非常耗時(shí)心软。
最后壕吹,從一個(gè)比較實(shí)際的例子講一下整個(gè)流程:CPU 開始運(yùn)行你的程序,你可能會(huì)讓 CPU 從 本地加載一張 PNG 的圖片并且解壓它删铃。這所有的事情都在 CPU 上進(jìn)行耳贬,然后當(dāng)你需要顯示解壓縮后的圖片時(shí),它需要以某種方式上傳到 GPU猎唁。還有一些看似平凡的咒劲,比如顯示文本,對(duì) CPU 來說卻是一件非常復(fù)雜的事情诫隅,所以 CPU 會(huì)調(diào)用 GPU 上的 一些框架(Core Text 和 Core Graphics)去生成一個(gè)位圖缎患,作為一個(gè)圖層上傳到 GPU 并準(zhǔn)備顯示出來。當(dāng)你滾動(dòng)或者在屏幕上移動(dòng)文本時(shí)阎肝,同樣的圖層能夠被復(fù)用挤渔,CPU 只需簡(jiǎn)單的告訴 GPU 新的位置就行了,GPU 就可以重用存在的圖層了风题,并不需要重新渲染判导,位圖也不需要重新上傳到 GPU嫉父。
5. 圖像的合成
在圖形世界中,大家都知道眼刃,一張完整的畫面是有很多個(gè)圖層組合在一起的绕辖,但實(shí)際的物理設(shè)備上只有一個(gè)屏幕,所以對(duì)于屏幕上的每一個(gè)像素擂红,GPU 需要算出怎么混合這些圖層來得到像素 RGB 的值仪际。
如果我們有第二個(gè)圖層放在第一個(gè)圖層之上,然后GPU將會(huì)把第二個(gè)圖層合成到第一個(gè)圖層中昵骤。有很多種不同的合成方法树碱,但是如果我們假定兩個(gè)圖層的像素對(duì)齊,并且使用正常的混合模式变秦,我們便可以用下面這個(gè)公式來計(jì)算每一個(gè)像素:
R = S + D * ( 1 – Sa )
6. 不透明 VS 透明
通過上面的公式我們可以知道:當(dāng)源圖層是完全不透明的時(shí)候成榜,目標(biāo)像素就等于源圖層。這可以省下 GPU 很大的工作量蹦玫,這樣只需簡(jiǎn)單的拷貝源圖層而不需要合成所有的像素值赎婚。(不需要考慮它下方的任何東西,因?yàn)槎急凰趽踝×?。
7. 像素對(duì)齊VS 不重合在一起
之前我們考慮的都是基于像素對(duì)其的圖片樱溉,當(dāng)所有的像素是對(duì)齊的時(shí)候我們得到上訴相對(duì)簡(jiǎn)單的計(jì)算公式挣输。我們只需要考慮在這個(gè)像素之上的所有圖層中對(duì)應(yīng)的單個(gè)像素,并把這些像素合并到一起福贞×媒溃或者,如果最頂層的圖層是不透明的肚医,這時(shí)候 GPU 就可以簡(jiǎn)單的拷貝它的像素到屏幕上。
那么為什么會(huì)照成像素不對(duì)其呢向瓷?主要有兩個(gè)原因可能會(huì)造成不對(duì)齊肠套。
第一個(gè)便是縮放;當(dāng)一個(gè)圖層放大縮小的時(shí)候猖任,圖層的像素便不會(huì)和屏幕的像素排列對(duì)齊你稚。
另一個(gè)原因便是當(dāng)圖層的起點(diǎn)不在一個(gè)像素的邊界上。
(Plus 上是3倍像素朱躺,如果我們?cè)诰幊讨性O(shè)置距離為 0.5 的高度就可能出現(xiàn)半個(gè)像素點(diǎn)刁赖,照成不對(duì)其)
在這兩種情況下,GPU 需要再做額外的計(jì)算长搀。它需要將源圖層上多個(gè)像素混合起來宇弛,生成一個(gè)用來合成的值。當(dāng)所有的像素都是對(duì)齊的時(shí)候源请,GPU 只剩下很少的工作要做枪芒。
8. Masks 蒙版實(shí)現(xiàn)“遮罩”效果
在 iOS 中Masks有幾個(gè)特點(diǎn):
在 Mask 范圍之外的圖層將不被顯示
Mask 的透明度為 0 時(shí)圖層不顯示或不顯示彻况,當(dāng)其為 0 < alpha < 1 時(shí)圖層為半透明狀態(tài)
我們來看一個(gè)實(shí)際例子:sketch
ResultLayer = ContentLayer * MaskLayer_Alpha
9. 離屏渲染(Offscreen Rendering)
GPU 渲染有兩種方式,一種是 On-Screen Rendering:意為當(dāng)前屏幕渲染舅踪,指的是GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)中進(jìn)行纽甘。
另外一種是 Off-Screen Rendering: 意為離屏渲染,指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開辟一個(gè)緩沖區(qū)進(jìn)行渲染操作抽碌。
還有一種特殊的離屏渲染方式:CPU 渲染在 iOS 中如果我們重寫了 -drawRect: 方法悍赢,并且使用任何Core Graphics的技術(shù)進(jìn)行了繪制操作,整個(gè)渲染過程由 CPU 完成货徙。
當(dāng)使用離屏渲染的時(shí)候會(huì)很容易照成性能消耗左权,主要是屏幕外緩沖區(qū)和屏幕緩沖區(qū)上下文環(huán)境的切換很耗性能。所以經(jīng)常會(huì)導(dǎo)致我們的 App 達(dá)不到每秒達(dá)到 60 幀的標(biāo)準(zhǔn)破婆。
10. JPEG VS PNG
每個(gè)人都知道 JPEG涮总。它是相機(jī)的產(chǎn)物。它代表著照片如何存儲(chǔ)在電腦上祷舀。
將 JPEG 數(shù)據(jù)轉(zhuǎn)換成像素?cái)?shù)據(jù)是一個(gè)非常復(fù)雜的過程瀑梗,他的解壓很慢,如果你將 JPEG 文件創(chuàng)建一個(gè) UIImage 并且繪制到屏幕上時(shí)裳扯,將會(huì)有一個(gè)延時(shí)抛丽,因?yàn)?CPU 這時(shí)候忙于解壓這個(gè) JPEG。特別是在列表里你饰豺,每個(gè) Cell 都需要解壓 JPEG亿鲜,那么你的滾動(dòng)當(dāng)然不會(huì)平滑會(huì)變的非吃┒郑卡蒿柳。
PNG讀作”ping”。和 JPEG 相反漩蟆,它的壓縮對(duì)格式是無(wú)損的垒探,且解碼 PNG 數(shù)據(jù)比解碼 JPEG 簡(jiǎn)單的多。因?yàn)槭菬o(wú)損的怠李,像素還原非常好圾叼,所以對(duì)于像程序中的原圖(如buttons,icons)捺癞,我們要用 PNG 格式的圖片夷蚊。
優(yōu)化總結(jié):
保持圖層的像素對(duì)齊
盡量保持圖層的透明度為 1
盡量避免離屏渲染
我們還是要把盡可能多的繪制工作交給 GPU 做,而讓 CPU 盡可能的來執(zhí)行應(yīng)用程序髓介。通常惕鼓,GPU 的渲染性能要比 CPU 高效很多,同時(shí)對(duì)系統(tǒng)的負(fù)載和消耗也更低一些唐础。
選擇合適的圖片格式