OpenGL ES 框架詳細解析(十) —— 使用頂點數據的最佳做法

版本記錄

版本號 時間
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所示。

Figure 8-1 Conversion of attribute data to shader variables

您的應用程序可以將屬性配置為常量,這意味著對于作為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應用程序應該使用諸如glColor4ubglTexCoord2f的每頂點屬性函數。


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_FLOATGL_INT_2_10_10_10_REV。 這些通常為諸如法線等屬性提供足夠的精度,占用的空間小于GL_FLOAT。

如果指定較小的組件,請確保重新排列頂點格式,以避免頂點數據不對齊。 請參閱Avoid Misaligned Vertex Data


Use Interleaved Vertex Data - 使用交錯頂點數據

您可以將頂點數據指定為一系列數組(也稱為數組結構),也可以將數組指定為每個元素包含多個屬性(結構數組)的數組。 iOS上的首選格式是具有單個交錯頂點格式的結構數組。 交錯數據為每個頂點提供更好的內存位置。

Figure 8-2 Interleaved memory structures place all data for a vertex together in memory

此規則的一個例外是當您的應用程序需要以不同于其余頂點數據的速率更新某些頂點數據時,或者如果某些數據可以在兩個或多個模型之間共享。 在任一情況下,您可能需要將屬性數據分成兩個或多個結構。

Figure 8-3 Use multiple vertex structures when some data is used differently

Avoid Misaligned Vertex Data - 避免不對齊的頂點數據

當您設計頂點結構時,將每個屬性的開始對齊為一個偏移量,該偏移量是其組件大小或4個字節的倍數,以較大者為準。 當屬性不對齊時,iOS必須在將數據傳遞到圖形硬件之前執行其他處理。

在圖8-4中,位置和正常數據分別定義為三個短整數,總共六個字節。 正常數據從偏移量6開始,這是本機大小(2字節)的倍數,但不是4字節的倍數。 如果這個頂點數據被提交到iOS,iOS將不得不花費更多的時間在將數據傳遞到硬件之前進行復制和對齊。 要解決這個問題,在每個屬性之后明確添加兩個字節的填充。

Figure 8-4 Align Vertex Data to avoid additional processing

Use Triangle Strips to Batch Vertex Data - 使用三角條綁定頂點數據

使用三角形條可以顯著減少OpenGL ES必須在模型上執行的頂點計算次數。 在圖8-5的左側,使用總共九個頂點指定三個三角形。 C,E和G實際上指定相同的頂點! 通過將數據指定為三角形條,可以將頂點數從9個減少到5個。

Figure 8-5 Triangle strip

有時,您的應用程序可以將多個三角形條組合成一個更大的三角形條。 所有條帶必須共享相同的渲染要求。 意即:

  • 您必須使用相同的著色器來繪制所有的三角形條。
  • 您必須能夠渲染所有的三角形條,而不會改變任何OpenGL狀態。
  • 三角形條必須共享相同的頂點屬性。

要合并兩個三角形條,請復制第一個條帶的最后一個頂點和第二個條帶的第一個頂點,如圖8-6所示。 當該條提交到OpenGL ES時,三角形DEE,EEF,EFF和FFG被認為是退化的,不進行處理或光柵化。

Figure 8-6 Use degenerate triangles to merge triangle strips

為獲得最佳性能,您的模型應作為單個索引三角形條提交。 為了避免在頂點緩沖區中多次指定相同頂點的數據,請使用單獨的索引緩沖區,并使用glDrawElements函數繪制三角形條(如果適用,則使用glDrawElementsInstanceglDrawRangeElements函數)。

在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-4Listing8-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_DRAWGL_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顯示了具有兩個頂點數組對象的示例配置。 每個配置獨立于另一個; 每個頂點數組對象可以引用一組不同的頂點屬性,它們可以存儲在同一個頂點緩沖區對象中,也可以跨越多個頂點緩沖對象。

Figure 8-7 Vertex array object configuration

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的時間。

后記

未完,待續~~~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容