版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2017.10.02 |
前言
OpenGL ES是一個強大的圖形庫,是跨平臺的圖形API,屬于OpenGL的一個簡化版本。iOS系統可以利用OpenGL ES將圖像數據直接送入到GPU進行渲染,這樣避免了從CPU進行計算再送到顯卡渲染帶來的性能的高消耗,能帶來來更好的視頻效果和用戶體驗。接下來幾篇就介紹下iOS 系統的 OpenGL ES框架。感興趣的可以看上面幾篇。
1. OpenGL ES 框架詳細解析(一) —— 基本概覽
2. OpenGL ES 框架詳細解析(二) —— 關于OpenGL ES
3. OpenGL ES 框架詳細解析(三) —— 構建用于iOS的OpenGL ES應用程序的清單
4. OpenGL ES 框架詳細解析(四) —— 配置OpenGL ES的上下文
5. OpenGL ES 框架詳細解析(五) —— 使用OpenGL ES和GLKit進行繪制
6. OpenGL ES 框架詳細解析(六) —— 繪制到其他渲染目的地
7. OpenGL ES 框架詳細解析(七) —— 多任務,高分辨率和其他iOS功能
8. OpenGL ES 框架詳細解析(八) —— OpenGL ES 設計指南
9. OpenGL ES 框架詳細解析(九) —— 調整您的OpenGL ES應用程序
Best Practices for Working with Vertex Data - 使用頂點數據的最佳做法
要使用OpenGL ES渲染幀,您的應用程序會配置圖形管道并提交要繪制的圖形基元。 在某些應用程序中,所有原語都使用相同的流水線配置繪制; 其他應用程序可以使用不同的技術來渲染框架的不同元素。 但是,無論您在應用程序中使用哪些基元,或者如何配置管道,您的應用程序都會為OpenGL ES提供頂點。 本章提供了頂點數據的刷新,并針對如何有效地處理頂點數據的目標建議進行跟蹤。
頂點由一個或多個屬性組成,如位置,顏色,正常或紋理坐標。 OpenGL ES 2.0或3.0應用程序可以自由定義自己的屬性; 頂點數據中的每個屬性對應于作為頂點著色器的輸入的屬性變量。 OpenGL 1.1應用程序使用固定功能管道定義的屬性。
您將屬性定義為由一到四個組件組成的向量。 屬性中的所有組件共享一個公共數據類型。 例如,顏色可以被定義為四個GLubyte
組件(紅色,綠色,藍色,alpha)。 當屬性加載到著色器變量中時,應用程序數據中未提供的任何組件都將使用OpenGL ES的默認值填充。 最后一個組件填充1,其他未指定的組件填充0,如圖8-1所示。
您的應用程序可以將屬性配置為常量,這意味著對于作為draw命令的一部分而提交的所有頂點使用相同的值,也可以是數組,這意味著每個頂點都是該屬性的值。 當您的應用程序在OpenGL ES中調用一個函數來繪制一組頂點時,頂點數據將從應用程序復制到圖形硬件。 圖形硬件比作用于頂點數據,處理著色器中的每個頂點,組裝原語并將其光柵化到幀緩沖區中。 OpenGL ES的一個優點在于,它將一組功能標準化,將頂點數據提交給OpenGL ES,從而消除OpenGL提供的更老且更低效的機制。
必須提交大量圖元以呈現幀的應用程序需要仔細管理其頂點數據以及如何將其提供給OpenGL ES。 本章中描述的做法可以歸納為幾個基本原則:
- 減小頂點數據的大小。
- 減少在OpenGL ES可以將頂點數據傳輸到圖形硬件之前必須進行的預處理。
- 減少將頂點數據復制到圖形硬件的時間。
- 減少對每個頂點執行的計算。
Simplify Your Models - 簡化您的模型
基于iOS設備的圖形硬件非常強大,但它顯示的圖像通常非常小。 您不需要非常復雜的模型來在iOS上展示令人信服的圖形。 減少用于繪制模型的頂點數量直接減少頂點數據的大小和對頂點數據執行的計算。
您可以使用以下一些技術來降低模型的復雜性:
- 以不同級別的細節提供模型的多個版本,并根據對象與相機的距離和顯示尺寸在運行時選擇適當的模型。
- 使用紋理消除對某些頂點信息的需求。 例如,凹凸貼圖可以用于在不添加更多頂點數據的情況下向模型添加細節。
- 一些模型添加頂點以改善照明細節或渲染質量。 通常在光柵化階段對每個頂點計算值并在三角形內插插值時完成此操作。 例如,如果您將聚光燈指向三角形的中心,則其效果可能不會被忽視,因為聚光燈中最亮的部分不會指向頂點。 通過添加頂點,您可以提供額外的插值點,代價是增加頂點數據的大小和對模型執行的計算。 不添加額外的頂點,而是考慮將計算移動到管道的片段階段中:
- 如果您的應用程序使用
OpenGL ES 2.0
或更高版本,則應用程序會在頂點著色器中執行計算,并將其分配給變量。 變化值由圖形硬件插值,并作為輸入傳遞給片段著色器。 相反,將計算的輸入分配給變量,并在片段著色器中執行計算。 這樣做會將從每頂點成本執行該計算的成本更改為每個片段成本,從而降低頂點階段的壓力,并減少管道片段階段上的壓力。 當您的應用程序在頂點處理中被阻塞時,執行此操作,計算價格便宜,并且可以通過更改顯著減少頂點計數。 - 如果您的應用程序使用
OpenGL ES 1.1
,您可以使用DOT3照明來執行每個片段的照明。 您可以通過添加凹凸貼圖紋理來保存正常信息,并使用GL_DOT3_RGB
模式的紋理組合操作應用凹凸貼圖。
- 如果您的應用程序使用
Avoid Storing Constants in Attribute Arrays - 避免在屬性數組中存儲常量
如果您的模型包含使用在整個模型中保持不變的數據的屬性,則不要為每個頂點復制該數據。 OpenGL ES 2.0和3.0應用程序可以設置不變的頂點屬性,也可以使用均勻的著色器值來保存該值。 OpenGL ES 1.1應用程序應該使用諸如glColor4ub
或glTexCoord2f
的每頂點屬性函數。
Use the Smallest Acceptable Types for Attributes - 使用最小可接受類型的屬性
指定每個屬性組件的大小時,請選擇提供可接受結果的最小數據類型。 以下是一些準則:
- 使用四個無符號字節組件(
GL_UNSIGNED_BYTE
)指定頂點顏色。 - 使用2或4個無符號字節(
GL_UNSIGNED_BYTE
)或無符號短(GL_UNSIGNED_SHORT
)指定紋理坐標。 不要將多組紋理坐標包裝到單個屬性中。 - 避免使用OpenGL ES
GL_FIXED
數據類型。 它需要與GL_FLOAT相同的內存量,但提供較小的值范圍。 所有iOS設備都支持硬件浮點數,因此可以更快地處理浮點值。 - OpenGL ES 3.0上下文支持更廣泛的小數據類型,例如
GL_HALF_FLOAT
和GL_INT_2_10_10_10_REV
。 這些通常為諸如法線等屬性提供足夠的精度,占用的空間小于GL_FLOAT。
如果指定較小的組件,請確保重新排列頂點格式,以避免頂點數據不對齊。 請參閱Avoid Misaligned Vertex Data。
Use Interleaved Vertex Data - 使用交錯頂點數據
您可以將頂點數據指定為一系列數組(也稱為數組結構),也可以將數組指定為每個元素包含多個屬性(結構數組)的數組。 iOS上的首選格式是具有單個交錯頂點格式的結構數組。 交錯數據為每個頂點提供更好的內存位置。
此規則的一個例外是當您的應用程序需要以不同于其余頂點數據的速率更新某些頂點數據時,或者如果某些數據可以在兩個或多個模型之間共享。 在任一情況下,您可能需要將屬性數據分成兩個或多個結構。
Avoid Misaligned Vertex Data - 避免不對齊的頂點數據
當您設計頂點結構時,將每個屬性的開始對齊為一個偏移量,該偏移量是其組件大小或4個字節的倍數,以較大者為準。 當屬性不對齊時,iOS必須在將數據傳遞到圖形硬件之前執行其他處理。
在圖8-4中,位置和正常數據分別定義為三個短整數,總共六個字節。 正常數據從偏移量6開始,這是本機大小(2字節)的倍數,但不是4字節的倍數。 如果這個頂點數據被提交到iOS,iOS將不得不花費更多的時間在將數據傳遞到硬件之前進行復制和對齊。 要解決這個問題,在每個屬性之后明確添加兩個字節的填充。
Use Triangle Strips to Batch Vertex Data - 使用三角條綁定頂點數據
使用三角形條可以顯著減少OpenGL ES必須在模型上執行的頂點計算次數。 在圖8-5的左側,使用總共九個頂點指定三個三角形。 C,E和G實際上指定相同的頂點! 通過將數據指定為三角形條,可以將頂點數從9個減少到5個。
有時,您的應用程序可以將多個三角形條組合成一個更大的三角形條。 所有條帶必須共享相同的渲染要求。 意即:
- 您必須使用相同的著色器來繪制所有的三角形條。
- 您必須能夠渲染所有的三角形條,而不會改變任何OpenGL狀態。
- 三角形條必須共享相同的頂點屬性。
要合并兩個三角形條,請復制第一個條帶的最后一個頂點和第二個條帶的第一個頂點,如圖8-6
所示。 當該條提交到OpenGL ES時,三角形DEE,EEF,EFF和FFG
被認為是退化的,不進行處理或光柵化。
為獲得最佳性能,您的模型應作為單個索引三角形條提交。 為了避免在頂點緩沖區中多次指定相同頂點的數據,請使用單獨的索引緩沖區,并使用glDrawElements
函數繪制三角形條(如果適用,則使用glDrawElementsInstance
或glDrawRangeElements
函數)。
在OpenGL ES 3.0中,您可以使用原始重新啟動功能來合并三角形條,而不使用簡并三角形。 當啟用此功能時,OpenGL ES將索引緩沖區中最大可能的值視為完成一個三角形條并啟動另一個三角形條的命令。 List 8-1
顯示了這種方法。
// Using primitive restart in OpenGL ES 3.0
// Prepare index buffer data (not shown: vertex buffer data, loading vertex and index buffers)
GLushort indexData[11] = {
0, 1, 2, 3, 4, // triangle strip ABCDE
0xFFFF, // primitive restart index (largest possible GLushort value)
5, 6, 7, 8, 9, // triangle strip FGHIJ
};
// Draw triangle strips
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glDrawElements(GL_TRIANGLE_STRIP, 11, GL_UNSIGNED_SHORT, 0);
在可能的情況下,對頂點和索引數據進行排序,因此共享共同頂點的三角形在三角形條中相互接近。 圖形硬件通常會緩存最近的頂點計算,以避免重新計算頂點。
Use Vertex Buffer Objects to Manage Copying Vertex Data - 使用頂點緩沖區對象來管理復制頂點數據
Listing 8-2
提供了一個簡單應用程序可用于向頂點著色器提供位置和顏色數據的函數。 它啟用兩個屬性并配置每個屬性以指向交錯頂點結構。 最后,它調用glDrawElements
函數將模型渲染為單個三角形條。
// Listing 8-2 Submitting vertex data to a shader program
typedef struct _vertexStruct
{
GLfloat position[2];
GLubyte color[4];
} vertexStruct;
void DrawModel()
{
const vertexStruct vertices[] = {...};
const GLubyte indices[] = {...};
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE,
sizeof(vertexStruct), &vertices[0].position);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE,
sizeof(vertexStruct), &vertices[0].color);
glEnableVertexAttribArray(GLKVertexAttribColor);
glDrawElements(GL_TRIANGLE_STRIP, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, indices);
}
此代碼有效,但效率低下。 每次調用DrawModel時,將索引和頂點數據復制到OpenGL ES中,并傳輸到圖形硬件。 如果頂點數據在調用之間沒有變化,這些不必要的副本可能會影響性能。 為避免不必要的副本,您的應用程序應將其頂點數據存儲在頂點緩沖對象(VBO)
中。 由于OpenGL ES擁有頂點緩沖區對象的內存,因此可將緩沖區存儲在圖形硬件更易于訪問的內存中,或將數據預處理為圖形硬件的首選格式。
注意:在OpenGL ES 3.0中使用頂點數組對象時,還必須使用頂點緩沖對象。
Listing 8-3
創建了一對頂點緩沖對象,一個用于保存頂點數據,另一個用于條帶的索引。 在每種情況下,代碼生成一個新對象,將其綁定為當前緩沖區,并填充緩沖區。 當應用程序初始化時,將調用CreateVertexBuffers
。
// Listing 8-3 Creating a vertex buffer object
GLuint vertexBuffer;
GLuint indexBuffer;
void CreateVertexBuffers()
{
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
}
Listing 8-4
將Listing8-2
修改為使用頂點緩沖對象。 Listing 8-4
的主要區別在于,glVertexAttribPointer
函數的參數不再指向頂點數組。 相反,每個都是頂點緩沖區對象的偏移量。
// Listing 8-4 Drawing with a vertex buffer object
void DrawModelUsingVertexBuffers()
{
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE,
sizeof(vertexStruct), (void *)offsetof(vertexStruct, position));
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE,
sizeof(vertexStruct), (void *)offsetof(vertexStruct, color));
glEnableVertexAttribArray(GLKVertexAttribColor);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glDrawElements(GL_TRIANGLE_STRIP, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, (void*)0);
}
1. Buffer Usage Hints - 緩沖區使用提示
上一個例子初始化了頂點緩沖區一次,之后從不改變它的內容。 您可以更改頂點緩沖區的內容。 頂點緩沖對象設計的關鍵部分是應用程序可以通知OpenGL ES如何使用緩沖區中存儲的數據。 OpenGL ES實現可以使用這個提示來改變它用于存儲頂點數據的策略。 在Listing 8-3
中,對glBufferData
函數的每次調用都提供了一個使用提示作為最后一個參數。 將GL_STATIC_DRAW
傳遞給glBufferData
告訴OpenGL ES,這兩個緩沖區的內容從來不會改變,這給了OpenGL ES更多機會來優化數據的存儲方式和位置。
OpenGL ES規范定義了以下用例:
-
GL_STATIC_DRAW
用于渲染多次的頂點緩沖區,其內容被指定一次,從不改變。 -
GL_DYNAMIC_DRAW
用于渲染多次的頂點緩沖區,其內容在渲染循環期間發生變化。 -
GL_STREAM_DRAW
用于渲染少量次數然后被丟棄的頂點緩沖區。
在iOS中,GL_DYNAMIC_DRAW
和GL_STREAM_DRAW
是等效的。 您可以使用glBufferSubData
函數更新緩沖區內容,但這樣做會導致性能損失,因為它會刷新命令緩沖區并等待所有命令完成。 雙重或三重緩沖可以降低這種性能成本。 (請參閱Use Double Buffering to Avoid Resource Conflicts。)為獲得更好的性能,請使用OpenGL ES 3.0中的glMapBufferRange函數或OpenGL ES 2.0或1.1中的EXT_map_buffer_range擴展提供的相應函數。
如果您的頂點格式中的不同屬性需要不同的使用模式,則將頂點數據分割成多個結構,并為共享常見使用特征的每個屬性集分配一個單獨的頂點緩沖對象。 Listing 8-5
修改了上一個示例以使用單獨的緩沖區來保存顏色數據。 通過使用GL_DYNAMIC_DRAW
提示分配顏色緩沖區,OpenGL ES可以分配該緩沖區,使您的應用程序保持合理的性能。
// **Listing 8-5** Drawing a model with multiple vertex buffer objects
typedef struct _vertexStatic
{
GLfloat position[2];
} vertexStatic;
typedef struct _vertexDynamic
{
GLubyte color[4];
} vertexDynamic;
// Separate buffers for static and dynamic data.
GLuint staticBuffer;
GLuint dynamicBuffer;
GLuint indexBuffer;
const vertexStatic staticVertexData[] = {...};
vertexDynamic dynamicVertexData[] = {...};
const GLubyte indices[] = {...};
void CreateBuffers()
{
// Static position data
glGenBuffers(1, &staticBuffer);
glBindBuffer(GL_ARRAY_BUFFER, staticBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(staticVertexData), staticVertexData, GL_STATIC_DRAW);
// Dynamic color data
// While not shown here, the expectation is that the data in this buffer changes between frames.
glGenBuffers(1, &dynamicBuffer);
glBindBuffer(GL_ARRAY_BUFFER, dynamicBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(dynamicVertexData), dynamicVertexData, GL_DYNAMIC_DRAW);
// Static index data
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
}
void DrawModelUsingMultipleVertexBuffers()
{
glBindBuffer(GL_ARRAY_BUFFER, staticBuffer);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE,
sizeof(vertexStruct), (void *)offsetof(vertexStruct, position));
glEnableVertexAttribArray(GLKVertexAttribPosition);
glBindBuffer(GL_ARRAY_BUFFER, dynamicBuffer);
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE,
sizeof(vertexStruct), (void *)offsetof(vertexStruct, color));
glEnableVertexAttribArray(GLKVertexAttribColor);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glDrawElements(GL_TRIANGLE_STRIP, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, (void*)0);
}
Consolidate Vertex Array State Changes Using Vertex Array Objects - 使用頂點數組對象合并頂點數組狀態更改
仔細看看Listing 8-5
中的DrawModelUsingMultipleVertexBuffers
函數。 它支持許多屬性,綁定多個頂點緩沖對象,并配置屬性以指向緩沖區。 所有這些初始化代碼基本上是靜態的; 沒有一個參數從幀到幀變化。 如果每次應用程序渲染幀時都調用此函數,則會重新配置圖形管道,導致了很多不必要的開銷。 如果應用程序繪制了許多不同類型的模型,則重新配置管道可能會成為瓶頸。 而是使用頂點數組對象來存儲一個完整的屬性配置。 頂點數組對象是OpenGL ES 3.0核心規范的一部分,可通過OES_vertex_array_object
擴展在OpenGL ES 2.0和1.1中提供。
Figure 8-7
顯示了具有兩個頂點數組對象的示例配置。 每個配置獨立于另一個; 每個頂點數組對象可以引用一組不同的頂點屬性,它們可以存儲在同一個頂點緩沖區對象中,也可以跨越多個頂點緩沖對象。
Listing 8-6
提供了用于配置上面顯示的第一個頂點數組對象的代碼。 它為新的頂點數組對象生成一個標識符,然后將頂點數組對象綁定到上下文。 之后,它會調用配置頂點屬性,如果代碼沒有使用頂點數組對象。 配置存儲到綁定的頂點數組對象而不是上下文。
// Listing 8-6 Configuring a vertex array object
void ConfigureVertexArrayObject()
{
// Create and bind the vertex array object.
glGenVertexArrays(1,&vao1);
glBindVertexArray(vao1);
// Configure the attributes in the VAO.
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE,
sizeof(staticFmt), (void*)offsetof(staticFmt,position));
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_UNSIGNED_SHORT, GL_TRUE,
sizeof(staticFmt), (void*)offsetof(staticFmt,texcoord));
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE,
sizeof(staticFmt), (void*)offsetof(staticFmt,normal));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glBindBuffer(GL_ARRAY_BUFFER, vbo2);
glVertexAttribPointer(GLKVertexAttribColor, 4, GL_UNSIGNED_BYTE, GL_TRUE,
sizeof(dynamicFmt), (void*)offsetof(dynamicFmt,color));
glEnableVertexAttribArray(GLKVertexAttribColor);
// Bind back to the default state.
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArray(0);
}
要繪制,代碼綁定頂點數組對象,然后像以前一樣提交繪圖命令。
注意:在OpenGL ES 3.0中,不允許頂點數組數據的客戶端存儲 - 頂點數組對象必須使用頂點緩沖對象。
為了獲得最佳性能,您的應用程序應該配置每個頂點數組對象一次,而不要在運行時更改它。 如果您需要在每個幀中更改頂點數組對象,則可以創建多個頂點數組對象。 例如,使用雙緩沖的應用程序可能為奇數幀配置一組頂點數組對象,而為偶數幀配置第二組。 每組頂點數組對象將指向用于呈現該幀的頂點緩沖區對象。 當頂點數組對象的配置不會改變時,OpenGL ES可以緩存有關頂點格式的信息,并改進如何處理這些頂點屬性。
Map Buffers into Client Memory for Fast Updates - 將緩沖區映射到客戶端內存中,實現快速更新
OpenGL ES應用程序設計中更具挑戰性的問題之一是使用動態資源,特別是如果您的頂點數據需要更改每個幀。 高效地平衡CPU和GPU之間的并行性需要仔細地管理應用程序內存空間和OpenGL ES內存之間的數據傳輸。 傳統技術,例如使用glBufferSubData
功能,可以降低性能,因為它們會迫使GPU在傳輸數據時等待,即使可能會從同一緩沖區其他位置的數據渲染。
例如,您可能需要修改頂點緩沖區并在每次通過高幀率渲染循環時繪制其內容。 渲染的最后一幀的繪圖命令可能仍在使用GPU,而CPU正在嘗試訪問緩沖存儲器以準備繪制下一幀,導致緩沖區更新調用阻止進一步的CPU工作,直到GPU完成。 您可以通過手動將CPU和GPU訪問同步到緩沖區來提高這種情況下的性能。
glMapBufferRange
函數提供了一種更有效的方式來動態更新頂點緩沖區。 (此函數可在OpenGL ES 3.0中使用,并通過OpenGL ES 1.1和2.0中的EXT_map_buffer_range擴展名使用)。使用此函數可以獲取指向OpenGL ES內存區域的指針,然后可以使用該指針來寫入新數據。glMapBufferRange
函數允許將緩沖區數據存儲的任何子范圍映射到客戶端內存中。 它還支持使用該功能與OpenGL同步對象時進行異步緩沖區修改的提示,如Listing 8-7
所示。
// Listing 8-7 Dynamically updating a vertex buffer with manual synchronization
GLsync fence;
GLboolean UpdateAndDraw(GLuint vbo, GLuint offset, GLuint length, void *data) {
GLboolean success;
// Bind and map buffer.
glBindBuffer(GL_ARRAY_BUFFER, vbo);
void *old_data = glMapBufferRange(GL_ARRAY_BUFFER, offset, length,
GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT |
GL_MAP_UNSYNCHRONIZED_BIT );
// Wait for fence (set below) before modifying buffer.
glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT,
GL_TIMEOUT_IGNORED);
// Modify buffer, flush, and unmap.
memcpy(old_data, data, length);
glFlushMappedBufferRange(GL_ARRAY_BUFFER, offset, length);
success = glUnmapBuffer(GL_ARRAY_BUFFER);
// Issue other OpenGL ES commands that use other ranges of the VBO's data.
// Issue draw commands that use this range of the VBO's data.
DrawMyVBO(vbo);
// Create a fence that the next frame will wait for.
fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
return success;
}
此示例中的UpdateAndDraw
函數使用glFenceSync
函數在提交使用特定緩沖區對象的繪圖命令之后立即建立同步點或柵欄。 然后使用glClientWaitSync
函數(在下一遍遍歷渲染循環中)來檢查該同步點,然后再修改緩沖區對象。 如果這些繪圖命令在渲染循環回來之前在GPU上完成執行,則CPU執行不會阻塞,UpdateAndDraw
函數繼續修改緩沖區并繪制下一個幀。 如果GPU尚未完成這些命令,則glClientWaitSync
函數將阻止進一步的CPU執行,直到GPU到達柵欄。 通過手動將同步點放置在代碼周圍的潛在資源沖突周圍,可以最大限度地減少CPU等待GPU的時間。
后記
未完,待續~~~