我們今天來講調試信息,這個東西講起來會比較無聊,因為都是一些函數調用,沒啥可講的,函數就是那樣用的,不過其效果挺好玩的,同時在程序設計中也是很必要的,所以還是來寫一下,不過,就是因為知識比較固定且簡單,所以我們 一篇就覆蓋三節的內容吧:
1. 獲取當前活動的頂點屬性和對應索引
2.獲取當前活動的uniform量和對應索引
2.Debug
Getting Ready
我們以上一節的旋轉三角形的例子來講這一篇。
當我們的shader program編譯連接完成之后,所有的量都已經分配了自己的索引,這時候我們可以查看其信息。
我們默認連接好的shader program的句柄為programHandle
Getting a list of active vertex input attributes and locations
我們需要獲取所有頂點屬性信息,首先要知道有多少個頂點屬性
glGetProgramInterfaceiv用來獲取program中的一些狀態信息。
它有四個參數:
第一個參數為:program 句柄,確定是哪一個程序中的狀態信息
第二個參數為:我們要查詢的是程序的哪一類數據資源
第三個參數為:我們要查詢的量
第四個參數為:存儲獲取的信息
glGetProgramInterfaceiv(programHandle, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &numAttribs);
即為,我們要獲取programHandle這個着色器程序中有關程序輸入(即為頂點屬性)的數據信息,將當前處於活動狀態的上述資源的數量( GL_ACTIVE_RESOURCES)存儲到numAttribs中
接下來,我們需要循環獲取頂點屬性(數據資源,resource)信息
假如我們的numAttribs的值為6,我們對於每一都如此做:
glGetProgramResourceiv獲取單個數據資源的狀態信息
它一共有8個參數:
para1:shader program 句柄用於指定處理的是哪一個程序
para2:同上面那個函數的第二個參數,數據資源的類型
para3:查詢的是第幾個數據資源(0 ~ numAttribs-1)
para5:要查詢數據資源的哪些信息,參數類型為枚舉數組地址,用於記錄要查詢的所有查詢信息
para4:para5中所傳入的數組的size
para6:para8的數組中一個數據資源包含多少個信息元素
para7:第七個參數是一個指向整數的指針,該整數將接收被寫入的數據組有多少組,如果para8為二維信息,即有很多組,每組數據為一維,那么需要該參數返回para8中有多少組,以便解析para8中的數據,而我們 這里的para8指定接收資源數據的一組信息,所以,此參數無效,我們可用nullptr來跳過該參數。
para8:用於接收系統返回的數據信息
glGetProgramResourceiv(programHandle, GL_PROGRAM_INPUT, i, 3, properties, 3, nullptr, results);
前兩個就不用說了,我們要獲取第 i 個數據資源(頂點屬性量)的信息,其中一組信息有三種,它們存儲在properties數組中:
GLenum properties[]{ GL_NAME_LENGTH, GL_TYPE, GL_LOCATION }; //數據資源的名字的長度,數據資源的數據類型,數據資源的索引值
我們需要將系統傳出的數據每3個一組存入results中,返回。由於我們的results只記錄一組返回信息,所以數據無需解析分組,所以para7為nullptr,如果為多組的二維信息,我們需要指定一個整型來接受返回數據。
如:glGetProgramResourceiv(programHandle, GL_PROGRAM_INPUT, i, 3, properties, 3, num, results);
num是一個GLint,那么意思是,results中存儲着num組數據,每組數據有3個,如同二維數組傳遞參數一樣,低維度的參數必須給定,也就是num前面的3,對吧~
最后,我們通過glGetProgramResourceiv獲取數據資源的名字。
我們已經通過上面那個函數獲取到了名字的長度,所以,我們創建一個緩沖區來接受字符串。
GLint nameBufSize = results[0] + 1; GLchar* name = new char[nameBufSize]; glGetProgramResourceName(programHandle, GL_PROGRAM_INPUT, i, nameBufSize, nullptr, name);
至此,我們獲取到了頂點屬性量的名字
我們的名字以及索引都得到了,還需要將GL_TYPE獲取到的類型枚舉值換成字符串
const GLchar* getTypeString(GLenum type) { // There are many more types than are covered here, but // these are the most common in these examples.
switch (type) { case GL_FLOAT: return "float"; case GL_FLOAT_VEC2: return "vec2"; case GL_FLOAT_VEC3: return "vec3"; case GL_FLOAT_VEC4: return "vec4"; case GL_DOUBLE: return "double"; case GL_INT: return "int"; case GL_UNSIGNED_INT: return "unsigned int"; case GL_BOOL: return "bool"; case GL_FLOAT_MAT2: return "mat2"; case GL_FLOAT_MAT3: return "mat3"; case GL_FLOAT_MAT4: return "mat4"; default: return "?"; } }
代碼如下:
void getVertexAttribList(GLint programHandle) { GLint numAttribs; glGetProgramInterfaceiv(programHandle, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &numAttribs); GLenum properties[]{ GL_NAME_LENGTH,GL_TYPE,GL_LOCATION }; cout << "Active attributes:" << endl; for (int i = 0; i < numAttribs; ++i) { GLint results[3]; glGetProgramResourceiv(programHandle, GL_PROGRAM_INPUT, i, 3, properties, 3, nullptr, results); GLint nameBufSize = results[0] + 1; GLchar* name = new char[nameBufSize]; glGetProgramResourceName(programHandle, GL_PROGRAM_INPUT, i, nameBufSize, nullptr, name); cout << "location:" << results[2] << " " << name << "(" << getTypeString(results[1]) << ")" << endl; delete[] name; } }
在main主程序中的着色器程序連接完成之后調用此函數即可得到如下測試結果:
沒問題,嘻嘻
Getting a list of active uniform variables
void getUniformList(GLint programHandle) { GLint numUniforms = 0; glGetProgramInterfaceiv(programHandle, GL_UNIFORM, GL_ACTIVE_RESOURCES, &numUniforms); GLenum properties[] = { GL_NAME_LENGTH, GL_TYPE, GL_LOCATION, GL_BLOCK_INDEX }; printf("Active uniforms:\n"); for (int i = 0; i < numUniforms; ++i) { GLint results[4]; glGetProgramResourceiv(programHandle, GL_UNIFORM, i, 4, properties, 4, NULL, results); if (results[3] != -1) continue; // Skip uniforms in blocks GLint nameBufSize = results[0] + 1; char * name = new char[nameBufSize]; glGetProgramResourceName(programHandle, GL_UNIFORM, i, nameBufSize, NULL, name); cout << "location:" << results[2] << " " << name << "(" << getTypeString(results[1]) << ")" << endl; delete[] name; } }
唯一不同的一點是properties的第四個枚舉量
它記錄了當前查詢的uniform量所在的uniform block,如果該uniform量不屬於任何block,那么該元素對應的值為-1
這里,我們只查詢單獨的uniform量,所以當其GL_BLOCK_INDEX對應的返回值為-1的時候是我們所要的,反之,我們跳過。
Debug
在之前,我們獲取調試信息的傳統方法是調用glGetError。但是這是一種非常繁瑣的方法,其繁瑣程度就不再贅述了。
從OpenGL 4.3開始,我們現在支持更現代的調試方法。我們可以整一個調試回調函數,該函數將在發生錯誤或者生成其他警告提示性消息時執行。
不僅如此,我們還可以發送自己的自定義消息,由同一個回調處理,我們可以使用各種條件過濾消息。
注:context有翻譯為上下文,有翻譯為環境,個人比較傾向於環境一說,下文不做翻譯
使用debug context 創建OpenGL程序。 雖然獲取debug context 並非絕對必要,但我們可能無法獲得與使用debug context 時相同的消息。
要在啟用調試的情況下使用GLFW創建OpenGL context,在創建窗口之前要使用以下函數調用。
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
默認情況下,OpenGL debug context 將啟用調試消息。但是,如果需要顯式啟用調試消息,請使用以下調用。
glEnable(GL_DEBUG_OUTPUT);
我們采用如下步驟進行:
1.創建回調函數以接收調試消息。
該函數必須符合OpenGL文檔中描述的特定原型。 我們采用如下方式:
void debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * message, void * param) { // Convert GLenum parameters to strings
printf("%s:%s[%s](%d): %s\n", sourceStr, typeStr, severityStr, id, message); }
2.使用glDebugMessageCallback向OpenGL注冊我們的回調:
glDebugMessageCallback(debugCallback,NULL);
3.啟用所有消息,所有來源,所有級別和所有ID:
glDebugMessageControl(GL_DONT_CARE,GL_DONT_CARE,
GL_DONT_CARE,0,NULL,GL_TRUE);
我們來解釋一下上述的一些內容:
回調函數debugCallback有幾個參數,其中最重要的是調試消息本身(第六個參數,message)。
對於此示例,我們只是將消息打印到標准輸出,但我們可以將其發送到日志文件或其他目的地.
debugCallback的前四個參數描述消息的來源,類型,ID號和嚴重性。
id號是特定於消息的無符號整數。 下表中描述了source,type和Severity參數的可能值。
Source | Generated By |
GL_DEBUG_SOURCE_API |
Calls to the OpenGL API |
GL_DEBUG_SOURCE_WINDOW_SYSTEM |
Calls to a window system API |
GL_DEBUG_SOURCE_THIRD_PARTY |
An application associated with OpenGL |
GL_DEBUG_SOURCE_APPLICATION |
This application itself. |
GL_DEBUG_SOURCE_OTHER |
Some other source |
Type | Description |
GL_DEBUG_TYPE_ERROR |
An error from the OpenGL API. |
GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR |
Behavior that has been deprecated |
GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR |
Undefined behaviour |
GL_DEBUG_TYPE_PORTABILITIY |
Some functionality is not portable. |
GL_DEBUG_TYPE_PERFORMANCE |
Possible performance issues |
GL_DEBUG_TYPE_MARKER |
An annotation |
GL_DEBUG_TYPE_PUSH_GROUP |
Messages related to debug group push. |
GL_DEBUG_TYPE_POP_GROUP |
Messages related to debug group pop. |
GL_DEBUG_TYPE_OTHER |
Other messages |
Severity | Meaning |
GL_DEBUG_SEVERITY_HIGH |
Errors or dangerous behaviour |
GL_DEBUG_SEVERITY_MEDIUM |
Major performance warnings, other warnings or use of deprecated functionality. |
GL_DEBUG_SEVERITY_LOW |
Redundant state changes, unimportant undefined behaviour. |
GL_DEBUG_SEVERITY_NOTIFICATION |
A notification, not an error or performance issue. |
length參數是消息字符串的長度,不包括空終止符。 最后一個參數param是用戶定義的指針。 我們可以使用它指向一些可能對回調函數有幫助的自定義對象。 例如,如果我們將消息記錄到文件中,則可能指向包含文件I / O功能的對象。可以使用glDebugMessageCallback的第二個參數設置此參數。
在debugCallback中,我們將每個GLenum參數轉換為字符串,此篇第一節中,我們寫過getTypeString這個函數。
然后,我們將所有信息打印到標准輸出。
對glDebugMessageCallback的調用使用OpenGL調試系統注冊我們的回調函數。
第一個參數是指向我們的回調函數的指針,第二個參數(本例中為NULL)可以是指向我們想要傳遞給回調的任何對象的指針。
每次調用debugCallback時,此指針都作為最后一個參數傳遞。
最后,對glDebugMessageControl的調用確定了我們的消息過濾器。
此功能可用於有選擇地打開或關閉消息源,類型,ID或嚴重性的任何組合。 在這個例子中,我們打開了一切。
一些擴展內容,可能不會用到,可跳過
OpenGL還為命名調試組的堆棧提供支持。 基本上這意味着我們可以記住堆棧上的所有調試消息過濾器設置,並在稍后進行一些更改后返回它們。 這可能很有用,例如,如果有一些代碼段我們需要過濾某些類型的消息,而其他段我們需要一組不同的消息。涉及的函數是glPushDebugGroup和glPopDebugGroup。 對glPushDebugGroup的調用會生成類型為GL_DEBUG_TYPE_PUSH_GROUP的調試消息,並在堆棧上保留調試過濾器的當前狀態。 然后我們可以使用glDebugMessageControl更改我們的過濾器,然后使用glPopDebugGroup返回到原始狀態。 類似地,函數glPopDebugGroup生成類型為GL_DEBUG_TYPE_POP_GROUP的調試消息。
第三部分主要是用於我們下一篇的類所用的,暫時沒有相關的測試
感覺像是在寫說明書,沒有辦法,這種章節的特點就是這樣,但又比較重要~
感謝您的閱讀,生活愉快~