調(diào)整您的OpenGL ES應(yīng)用程序
iOS中OpenGL ES應(yīng)用程序的性能與OS X或其他桌面操作系統(tǒng)中OpenGL的性能不同。雖然功能強(qiáng)大的計算設(shè)備,基于iOS設(shè)備的桌面或筆記本電腦不具備內(nèi)存或CPU功能。使用與典型臺式機(jī)或筆記本電腦GPU可能使用的算法不同的嵌入式GPU來優(yōu)化較低的內(nèi)存和功耗。低效渲染圖形數(shù)據(jù)可能會導(dǎo)致較差的幀速率或極大地降低基于iOS設(shè)備的電池壽命。
后面的章節(jié)介紹了許多提高應(yīng)用程序性能的技術(shù);本章涵蓋整體策略。除非另有說明,本章中的建議涉及OpenGL ES的所有版本。
使用Xcode和儀器調(diào)試和配置您的應(yīng)用程序
在各種設(shè)備上的各種場景中測試其性能之前,請勿優(yōu)化應(yīng)用程序。 Xcode和Instruments包括幫助您識別應(yīng)用程序中的性能和正確性問題的工具
監(jiān)視Xcode調(diào)試量表,了解性能的一般概述。當(dāng)您從Xcode運(yùn)行應(yīng)用程序時,可以看到這些儀表,以便在開發(fā)應(yīng)用程序時輕松發(fā)現(xiàn)性能變化。
使用儀器中的OpenGL ES Analysis和OpenGL ES驅(qū)動程序工具,以更深入地了解運(yùn)行時性能。獲取關(guān)于您的應(yīng)用程序的資源使用和符合OpenGL ES最佳做法的詳細(xì)信息,并選擇性地禁用部分圖形管道,以便您可以確定哪個部分是應(yīng)用程序中的重大瓶頸。有關(guān)詳細(xì)信息,請參閱“儀器用戶指南”。
在Xcode中使用OpenGL ES Frame Debugger和性能分析器工具來精確定位性能和渲染問題。捕獲用于渲染和呈現(xiàn)單個幀的所有OpenGL ES命令,然后通過這些命令查看每個OpenGL ES狀態(tài),綁定資源和輸出幀緩沖區(qū)的影響。您還可以查看著色器源代碼,編輯它,并查看更改如何影響渲染圖像。在支持OpenGL ES 3.0的設(shè)備上,F(xiàn)rame Debugger還指出哪些繪制調(diào)用和著色器指令對渲染時間最有貢獻(xiàn)。有關(guān)這些工具的更多信息,請參閱Xcode OpenGL ES工具概述。
注意 Xcode 和 Instruments 中的 OpenGL ES 錯誤
當(dāng)您的應(yīng)用程序錯誤地使用 OpenGL ES API(例如,通過請求底層硬件無法執(zhí)行的操作)時,會發(fā)生 OpenGL ES 錯誤。即使您的內(nèi)容呈現(xiàn)正確,這些錯誤也可能表明存在性能問題。檢查 OpenGL ES 錯誤的傳統(tǒng)方法是調(diào)用 glGetError 函數(shù);但是,重復(fù)調(diào)用此函數(shù)會顯著降低性能。相反,請使用上述工具來測試錯誤:
- 在 Instruments 中分析您的應(yīng)用程序時,請參閱 OpenGL ES 分析器工具的詳細(xì)信息窗格以查看記錄時報告的任何 OpenGL ES 錯誤。
- 在 Xcode 中調(diào)試您的應(yīng)用程序時,捕獲一個幀以檢查用于生成它的繪圖命令,以及在執(zhí)行這些命令時遇到的任何錯誤。
您還可以將 Xcode 配置為在遇到 OpenGL ES 錯誤時停止程序執(zhí)行。 (請參閱添加 OpenGL ES 錯誤斷點。)
注釋您的 OpenGL ES 代碼以進(jìn)行信息性調(diào)試和分析
您可以通過將 OpenGL ES 命令組織成邏輯組并向 OpenGL ES 對象添加有意義的標(biāo)簽來提高調(diào)試和分析的效率。這些組和標(biāo)簽出現(xiàn)在 Xcode 中的 OpenGL ES 幀調(diào)試器中,如圖 7-1 所示,以及儀器中的 OpenGL ES 分析器中。要添加組和標(biāo)簽,請使用 EXT_debug_marker 和 EXT_debug_label 擴(kuò)展
當(dāng)您有一系列表示單個有意義操作的繪圖命令時(例如,繪制游戲角色),您可以使用標(biāo)記將它們分組以進(jìn)行調(diào)試。清單 7-1 顯示了如何對場景的單個元素的紋理、程序、頂點數(shù)組和繪制調(diào)用進(jìn)行分組。首先,它調(diào)用 glPushGroupMarkerEXT 函數(shù)提供一個有意義的名稱,然后它發(fā)出一組 OpenGL ES 命令。最后,它通過調(diào)用 glPopGroupMarkerEXT 函數(shù)關(guān)閉組。
Listing 7-1 使用調(diào)試標(biāo)記來注釋繪圖命令
glPushGroupMarkerEXT(0, "Draw Spaceship");
glBindTexture(GL_TEXTURE_2D, _spaceshipTexture);
glUseProgram(_diffuseShading);
glBindVertexArrayOES(_spaceshipMesh);
glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT, 0);
glPopGroupMarkerEXT();
您可以使用多個嵌套標(biāo)記在復(fù)雜場景中創(chuàng)建有意義的組的層次結(jié)構(gòu)。當(dāng)您使用 GLKView 類繪制 OpenGL ES 內(nèi)容時,它會自動創(chuàng)建一個“渲染”組,其中包含您繪制方法中的所有命令。您創(chuàng)建的任何標(biāo)記都嵌套在該組中。
標(biāo)簽為 OpenGL ES 對象提供有意義的名稱,例如紋理、著色器程序和頂點數(shù)組對象。調(diào)用 glLabelObjectEXT 函數(shù)為對象指定一個名稱,以便在調(diào)試和分析時顯示。清單 7-2 說明了使用這個函數(shù)來標(biāo)記一個頂點數(shù)組對象。如果您使用 GLKTextureLoader 類加載紋理數(shù)據(jù),它會自動用文件名標(biāo)記它創(chuàng)建的 OpenGL ES 紋理對象。
glGenVertexArraysOES(1, &_spaceshipMesh);
glBindVertexArrayOES(_spaceshipMesh);
glLabelObjectEXT(GL_VERTEX_ARRAY_OBJECT_EXT, _spaceshipMesh, 0, "Spaceship");
一般性能建議
使用常識來指導(dǎo)您的性能調(diào)優(yōu)工作。例如,如果您的應(yīng)用程序每幀僅繪制幾十個三角形,則更改它提交頂點數(shù)據(jù)的方式不太可能提高其性能。尋找為您的工作提供最大性能改進(jìn)的優(yōu)化。
僅在場景數(shù)據(jù)更改時重繪場景
在渲染新幀之前,您的應(yīng)用程序應(yīng)該等到場景中的某些內(nèi)容發(fā)生變化。 Core Animation 緩存呈現(xiàn)給用戶的最后一個圖像并繼續(xù)顯示它,直到呈現(xiàn)新的幀。
即使您的數(shù)據(jù)發(fā)生變化,也沒有必要以硬件處理命令的速度渲染幀。對于用戶來說,較慢但固定的幀速率通常比快速但可變的幀速率更平滑。每秒 30 幀的固定幀速率足以滿足大多數(shù)動畫的需求,并有助于降低功耗。
禁用未使用的 OpenGL ES 功能
最好的計算是您的應(yīng)用程序永遠(yuǎn)不會執(zhí)行的計算。例如,如果可以預(yù)先計算結(jié)果并將其存儲在模型數(shù)據(jù)中,則可以避免在運(yùn)行時執(zhí)行該計算。
如果您的應(yīng)用程序是為 OpenGL ES 2.0 或更高版本編寫的,請不要創(chuàng)建具有大量開關(guān)和條件的單個著色器來執(zhí)行應(yīng)用程序渲染場景所需的每項任務(wù)。相反,編譯多個著色器程序,每個著色器程序執(zhí)行特定的、重點任務(wù)。
如果您的應(yīng)用程序使用 OpenGL ES 1.1,請禁用渲染場景所不需要的任何固定功能操作。例如,如果您的應(yīng)用不需要照明或混合,請禁用這些功能。同樣,如果您的應(yīng)用僅繪制 2D 模型,則應(yīng)禁用霧和深度測試。
簡化您的照明模型
這些指南既適用于 OpenGL ES 1.1 中的固定功能照明,也適用于您在 OpenGL ES 2.0 或更高版本中的自定義著色器中使用的基于著色器的照明計算。
為您的應(yīng)用程序使用盡可能少的燈光和最簡單的燈光類型??紤]使用平行光而不是聚光燈,后者需要更多的計算。著色器應(yīng)在模型空間中執(zhí)行光照計算;考慮在著色器中使用更簡單的光照方程而不是更復(fù)雜的光照算法。
預(yù)先計算您的照明并將顏色值存儲在可以通過片段處理進(jìn)行采樣的紋理中。
有效地使用基于 Tile 的延遲渲染
iOS 設(shè)備中使用的所有 GPU 都使用基于圖塊的延遲渲染 (TBDR)。當(dāng)您調(diào)用 OpenGL ES 函數(shù)向硬件提交渲染命令時,這些命令會被緩沖,直到積累了大量命令。在您呈現(xiàn)渲染緩沖區(qū)或刷新命令緩沖區(qū)之前,硬件不會開始處理頂點和著色像素。然后,它通過將幀緩沖區(qū)劃分為瓦片,然后為每個瓦片繪制一次命令,將這些命令作為單個操作渲染,每個瓦片僅渲染其中可見的圖元。 (GLKView 類在您的繪圖方法返回后呈現(xiàn)渲染緩沖區(qū)。如果您使用 CAEAGLLayer 類創(chuàng)建自己的用于顯示的渲染緩沖區(qū),則可以使用 OpenGL ES 上下文的 presentRenderbuffer: 方法來呈現(xiàn)它。glFlush 或 glFinish 函數(shù)會刷新命令緩沖區(qū)。)
由于 tile 內(nèi)存是 GPU 硬件的一部分,因此深度測試和混合等渲染過程的部分在時間和能源使用方面比傳統(tǒng)的基于流的 GPU 架構(gòu)更高效。由于這種架構(gòu)一次處理整個場景的所有頂點,因此 GPU 可以在處理片段之前執(zhí)行隱藏表面去除。不可見的像素在不采樣紋理或執(zhí)行片段處理的情況下被丟棄,顯著減少了 GPU 必須執(zhí)行以渲染圖塊的計算。
一些在傳統(tǒng)的基于流的渲染器上有用的渲染策略在 iOS 圖形硬件上有很高的性能成本。遵循以下指南可以幫助您的應(yīng)用在 TBDR 硬件上表現(xiàn)良好。
避免邏輯緩沖區(qū)加載和存儲
當(dāng) TBDR 圖形處理器開始渲染圖塊時,它必須首先將幀緩沖區(qū)該部分的內(nèi)容從共享內(nèi)存?zhèn)鬏數(shù)?GPU 上的圖塊內(nèi)存。這種內(nèi)存?zhèn)鬏敺Q為邏輯緩沖區(qū)加載,需要時間和精力。大多數(shù)情況下,繪制下一幀不需要幀緩沖區(qū)的先前內(nèi)容。每當(dāng)您開始渲染新幀時,都調(diào)用 glClear 來避免加載先前緩沖區(qū)內(nèi)容的性能成本。
同樣,當(dāng) GPU 完成渲染圖塊時,它必須將圖塊的像素數(shù)據(jù)寫回共享內(nèi)存。這種傳輸稱為邏輯緩沖存儲,也有性能成本。對于繪制的每一幀,至少需要一個這樣的傳輸——屏幕上顯示的顏色渲染緩沖區(qū)必須傳輸?shù)焦蚕韮?nèi)存,以便 Core Animation 呈現(xiàn)它。渲染算法中使用的其他幀緩沖區(qū)附件(例如,深度、模板和多重采樣緩沖區(qū))不需要保留,因為它們的內(nèi)容將在繪制的下一幀中重新創(chuàng)建。 OpenGL ES 會自動將這些緩沖區(qū)存儲到共享內(nèi)存中——這會導(dǎo)致性能成本——除非您明確地使它們無效。要使緩沖區(qū)無效,請使用 OpenGL ES 3.0 中的 glInvalidateFramebuffer 命令或 OpenGL ES 1.1 或 2.0 中的 glDiscardFramebufferEXT 命令。 (有關(guān)更多詳細(xì)信息,請參閱丟棄不需要的渲染緩沖區(qū)。)當(dāng)您使用 GLKView 類提供的基本繪制周期時,它會自動使其創(chuàng)建的任何可繪制深度、模板或多重采樣緩沖區(qū)無效。
如果切換渲染目標(biāo),也會發(fā)生邏輯緩沖區(qū)存儲和加載操作。如果渲染到紋理,然后渲染到視圖的幀緩沖區(qū),然后再次渲染到相同的紋理,則必須在共享內(nèi)存和 GPU 之間重復(fù)傳輸紋理的內(nèi)容。批處理您的繪圖操作,以便一起完成所有到渲染目標(biāo)的繪圖。當(dāng)您切換幀緩沖區(qū)(使用 glBindFramebuffer 或 glFramebufferTexture2D 函數(shù)或 bindDrawable 方法)時,使不需要的幀緩沖區(qū)附件無效以避免導(dǎo)致邏輯緩沖區(qū)存儲。
有效地使用隱藏表面去除
TBDR 圖形處理器自動使用深度緩沖區(qū)為整個場景執(zhí)行隱藏表面去除,確保每個像素只運(yùn)行一個片段著色器。不需要傳統(tǒng)的減少片段處理的技術(shù)。例如,從前到后按深度對對象或基元進(jìn)行排序會有效地重復(fù) GPU 完成的工作,從而浪費(fèi) CPU 時間。
當(dāng)啟用混合或 alpha 測試時,或者片段著色器使用丟棄指令或?qū)懭?gl_FragDepth 輸出變量時,GPU 無法執(zhí)行隱藏表面移除。在這些情況下,GPU 無法使用深度緩沖區(qū)來決定片段的可見性,因此它必須為覆蓋每個像素的所有基元運(yùn)行片段著色器,從而大大增加渲染幀所需的時間和能量。為避免這種性能成本,請盡量減少混合、丟棄指令和深度寫入的使用。
如果您無法避免混合、alpha 測試或丟棄指令,請考慮以下策略來減少它們的性能影響
- 按不透明度對對象進(jìn)行排序。先畫不透明的物體。接下來使用丟棄操作(或 OpenGL ES 1.1 中的 alpha 測試)繪制需要著色器的對象。最后,繪制 alpha 混合對象。
-
修剪需要混合或丟棄指令的對象以減少處理的片段數(shù)量。例如,不是繪制一個正方形來渲染包含大部分空白空間的 2D 精靈紋理,而是繪制一個更接近圖像形狀的多邊形,如圖 7-2 所示。額外頂點處理的性能成本遠(yuǎn)低于運(yùn)行其結(jié)果將不被使用的片段著色器的性能成本。
Figure7-2 - 盡早在片段著色器中使用丟棄指令,以避免執(zhí)行結(jié)果未使用的計算。
- 不要使用 alpha 測試或丟棄指令來殺死像素,而是使用 alpha 混合并將 alpha 設(shè)置為零。顏色幀緩沖區(qū)沒有被修改,但圖形硬件仍然可以使用它執(zhí)行的任何 Z 緩沖區(qū)優(yōu)化。這確實會更改存儲在深度緩沖區(qū)中的值,因此可能需要對透明圖元進(jìn)行從后到前的排序。
- 如果您的性能受到不可避免的丟棄操作的限制,請考慮“Z-Prepass”渲染策略。使用僅包含丟棄邏輯(避免昂貴的光照計算)的簡單片段著色器渲染一次場景以填充深度緩沖區(qū)。然后,使用 GL_EQUAL 深度測試功能和照明著色器再次渲染您的場景。盡管多通道渲染通常會導(dǎo)致性能損失,但與涉及大量丟棄操作的單通道渲染相比,這種方法可以產(chǎn)生更好的性能。
用于高效資源管理的 OpenGL ES 命令組
上述內(nèi)存帶寬和計算節(jié)省在處理大型場景時表現(xiàn)最佳。但是當(dāng)硬件接收到需要它渲染較小場景的 OpenGL ES 命令時,渲染器的效率就會大大降低。例如,如果您的應(yīng)用程序使用紋理渲染成批三角形,然后修改紋理,則 OpenGL ES 實現(xiàn)必須立即清除這些命令或復(fù)制紋理——這兩種選擇都不能有效地使用硬件。類似地,任何從幀緩沖區(qū)讀取像素數(shù)據(jù)的嘗試都需要處理前面的命令,如果它們會改變該幀緩沖區(qū)。
為避免這些性能損失,請組織 OpenGL ES 調(diào)用序列,以便同時執(zhí)行每個渲染目標(biāo)的所有繪圖命令
盡量減少繪圖命令的數(shù)量
每次您的應(yīng)用程序提交要由 OpenGL ES 處理的圖元時,CPU 都會為圖形硬件準(zhǔn)備命令。如果您的應(yīng)用程序使用許多 glDrawArrays 或 glDrawElements 調(diào)用來渲染場景,則其性能可能會受到 CPU 資源的限制,而沒有充分利用 GPU。
為了減少這種開銷,請尋找將渲染合并為更少繪制調(diào)用的方法。有用的策略包括:
- 將多個圖元合并為一個三角形帶,如使用三角形帶批處理頂點數(shù)據(jù)中所述。為獲得最佳效果,請合并在空間接近的情況下繪制的圖元。當(dāng)大型、龐大的模型在框架中不可見時,您的應(yīng)用程序更難以有效地剔除它們。
- 創(chuàng)建紋理圖集以使用同一紋理圖像的不同部分繪制多個圖元,如將紋理組合到紋理圖集中所述。
- 使用實例化繪圖來渲染許多類似的對象,如下所述。
使用實例化繪圖來最小化繪圖調(diào)用
實例化繪圖命令允許您使用單個繪圖調(diào)用多次繪制相同的頂點數(shù)據(jù)。與使用 CPU 時間來設(shè)置網(wǎng)格不同實例之間的變化(例如位置偏移、變換矩陣、顏色或紋理坐標(biāo))并為每個實例發(fā)出繪制命令不同,實例化繪制將實例變化的處理移動到著色器代碼中在 GPU 上運(yùn)行。
多次重復(fù)使用的頂點數(shù)據(jù)是實例化繪圖的主要候選對象。例如,清單 7-3 中的代碼在場景中的多個位置繪制一個對象。但是,許多 glUniform 和 glDrawArrays 調(diào)用會增加 CPU 開銷,從而降低性能。
for (x = 0; x < 10; x++) {
for (y = 0; y < 10; y++) {
glUniform4fv(uniformPositionOffset, 1, positionOffsets[x][y]);
glDrawArrays(GL_TRIANGLES, 0, numVertices);
}
}
采用實例化繪圖需要兩個步驟:首先,用對 glDrawArraysInstanced 或 glDrawElementsInstanced 的單個調(diào)用替換上述循環(huán)。這些調(diào)用在其他方面與 glDrawArrays 或 glDrawElements 相同,但有一個額外的參數(shù)指示要繪制的實例數(shù)(示例 7-3 中的示例為 100)。其次,選擇并實施 OpenGL ES 提供的兩種策略之一,用于在您的頂點著色器中使用每個實例的信息。
使用著色器實例 ID 策略,您的頂點著色器會派生或查找每個實例的信息。每次頂點著色器運(yùn)行時,它的 gl_InstanceID 內(nèi)置變量包含一個標(biāo)識當(dāng)前正在繪制的實例的數(shù)字。使用此數(shù)字計算著色器代碼中的位置偏移、顏色或其他每個實例的變化,或者在統(tǒng)一數(shù)組或其他大容量存儲中查找每個實例的信息。例如,清單 7-4 使用這種技術(shù)繪制了位于 10 x 10 網(wǎng)格中的網(wǎng)格的 100 個實例。
#version 300 es
in vec4 position;
uniform mat4 modelViewProjectionMatrix;
void main()
{
float xOffset = float(gl_InstanceID % 10) * 0.5 - 2.5;
float yOffset = float(gl_InstanceID / 10) * 0.5 - 2.5;
vec4 offset = vec4(xOffset, yOffset, 0, 0);
gl_Position = modelViewProjectionMatrix * (position + offset);
}
使用實例化數(shù)組策略,您將每個實例的信息存儲在頂點數(shù)組屬性中。然后您的頂點著色器可以訪問該屬性以利用每個實例的信息。調(diào)用 glVertexAttribDivisor 函數(shù)來指定該屬性在 OpenGL ES 繪制每個實例時如何前進(jìn)。清單 7-5 演示了為實例繪制設(shè)置頂點數(shù)組,清單 7-6 顯示了相應(yīng)的著色器。
glGenBuffers(1, &_instBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _instBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(instData), instData, GL_STATIC_DRAW);
glEnableVertexAttribArray(kMyInstanceDataAttrib);
glVertexAttribPointer(kMyInstanceDataAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribDivisor(kMyInstanceDataAttrib, 1);
#version 300 es
layout(location = 0) in vec4 position;
layout(location = 5) in vec2 inOffset;
uniform mat4 modelViewProjectionMatrix;
void main()
{
vec4 offset = vec4(inOffset, 0.0, 0.0)
gl_Position = modelViewProjectionMatrix * (position + offset);
}
實例化繪圖在核心 OpenGL ES 3.0 API 和 OpenGL ES 2.0 中通過 EXT_draw_instanced 和 EXT_instanced_arrays 擴(kuò)展可用。
最小化 OpenGL ES 內(nèi)存使用
您的 iOS 應(yīng)用程序與系統(tǒng)和其他 iOS 應(yīng)用程序共享主內(nèi)存。為 OpenGL ES 分配的內(nèi)存會減少應(yīng)用程序中可用于其他用途的內(nèi)存。考慮到這一點,只分配您需要的內(nèi)存,并在您的應(yīng)用不再需要它時立即釋放它。以下是一些可以節(jié)省內(nèi)存的方法:
- 將圖像加載到 OpenGL ES 紋理后,釋放原始圖像。
- 僅在您的應(yīng)用需要時分配深度緩沖區(qū)。
- 如果您的應(yīng)用程序不需要一次使用其所有資源,請僅加載項目的一個子集。例如,游戲可能分為多個級別;每個加載適合更嚴(yán)格資源限制的總資源的子集。
iOS 中的虛擬內(nèi)存系統(tǒng)不使用交換文件(當(dāng)內(nèi)存不足時,將內(nèi)存的數(shù)據(jù)copy到磁盤中,但內(nèi)存空間回復(fù)時,將磁盤中的內(nèi)存重新copy到內(nèi)存中)。當(dāng)檢測到內(nèi)存不足情況時,虛擬內(nèi)存不會將易失性頁面寫入磁盤,而是釋放非易失性內(nèi)存,為正在運(yùn)行的應(yīng)用程序提供所需的內(nèi)存。您的應(yīng)用程序應(yīng)努力使用盡可能少的內(nèi)存,并準(zhǔn)備好處理對您的應(yīng)用程序不重要的對象。 iOS 應(yīng)用程序編程指南中詳細(xì)介紹了對低內(nèi)存情況的響應(yīng)。
注意核心動畫合成性能
Core Animation 將渲染緩沖區(qū)的內(nèi)容與視圖層次結(jié)構(gòu)中的任何其他層進(jìn)行合成,無論這些層是使用 OpenGL ES、Quartz 還是其他圖形庫繪制的。這很有幫助,因為這意味著 OpenGL ES 是 Core Animation 的一等公民。但是,將 OpenGL ES 內(nèi)容與其他內(nèi)容混合需要時間;如果使用不當(dāng),您的應(yīng)用可能會執(zhí)行得太慢而無法達(dá)到交互式幀速率。
為獲得絕對最佳性能,您的應(yīng)用程序應(yīng)完全依賴 OpenGL ES 來呈現(xiàn)您的內(nèi)容。調(diào)整包含 OpenGL ES 內(nèi)容的視圖的大小以匹配屏幕,確保其 opaque 屬性設(shè)置為 YES(GLKView 對象的默認(rèn)值)并且沒有其他視圖或核心動畫層可見。
如果您渲染到合成在其他層之上的核心動畫層,使您的 CAEAGLLayer 對象不透明會降低(但不會消除)性能成本。如果您的 CAEAGLLayer 對象混合在層層次結(jié)構(gòu)中它下面的層的頂部,則渲染緩沖區(qū)的顏色數(shù)據(jù)必須采用預(yù)乘 alpha 格式才能由 Core Animation 正確合成。在其他內(nèi)容之上混合 OpenGL ES 內(nèi)容會導(dǎo)致嚴(yán)重的性能損失。