2012-01-23 87 views
21

我最近用頂點陣列對象(VAO)編寫了一些OpenGL 3.3代碼,稍後在英特爾圖形適配器上進行了測試,我發現我的失望是,元素數組緩衝區綁定顯然不是VAO狀態的一部分,作爲呼叫:VAO和元素陣列緩衝區狀態

glBindVertexArray(my_vao); 
glDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_INTEGER, 0); 

沒有效果,而:

glBindVertexArray(my_vao); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, my_index_buffer); // ? 
glDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_INTEGER, 0); 

呈現幾何形狀。我認爲這只是英特爾實現OpenGL時的一個小問題(因爲它在GL_ARB_vertex_array_object(甚至在GL_OES_vertex_array_object)中明確聲明元素數組保存狀態的一部分),但之後出現在移動NVIDIA Quadro 4200上。這就是不好玩。

它是我的代碼中的驅動程序錯誤,規格錯誤還是錯誤?代碼在GeForce 260和480上完美無缺地工作。

任何人都有類似的經驗?

還有一個奇怪的是,GL_EXT_direct_state_access沒有函數來將元素數組緩衝區綁定到VAO(但它確實有指定頂點attrib數組的函數,因此也有數組緩衝區)。 GPU製造商是否在破壞我們的規格和作弊行爲?或者是什麼?

編輯

我本來不打算表現出任何的源代碼,因爲我認爲這是沒有必要在這裏。但是,隨着要求,這裏是再現問題的最少測試用例:

static GLuint n_vertex_buffer_object, p_index_buffer_object_list[3]; 
static GLuint p_vao[2]; 

bool InitGLObjects() 
{ 
    const float p_quad_verts_colors[] = { 
     1, 0, 0, -1, 1, 0, 
     1, 0, 0, 1, 1, 0, 
     1, 0, 0, 1, -1, 0, 
     1, 0, 0, -1, -1, 0, // red quad 
     0, 0, 1, -1, 1, 0, 
     0, 0, 1, 1, 1, 0, 
     0, 0, 1, 1, -1, 0, 
     0, 0, 1, -1, -1, 0, // blue quad 
     0, 0, 0, -1, 1, 0, 
     0, 0, 0, 1, 1, 0, 
     0, 0, 0, 1, -1, 0, 
     0, 0, 0, -1, -1, 0 // black quad 
    }; 
    const unsigned int p_quad_indices[][6] = { 
     {0, 1, 2, 0, 2, 3}, 
     {4, 5, 6, 4, 6, 7}, 
     {8, 9, 10, 8, 10, 11} 
    }; 
    glGenBuffers(1, &n_vertex_buffer_object); 
    glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object); 
    glBufferData(GL_ARRAY_BUFFER, sizeof(p_quad_verts_colors), p_quad_verts_colors, GL_STATIC_DRAW); 
    glGenBuffers(3, p_index_buffer_object_list); 
    for(int n = 0; n < 3; ++ n) { 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[n]); 
     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(p_quad_indices[n]), p_quad_indices[n], GL_STATIC_DRAW); 
    } 

    glGenVertexArrays(2, p_vao); 
    glBindVertexArray(p_vao[0]); 
    { 
     glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object); 
     glEnableVertexAttribArray(0); 
     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(0)); 
     glEnableVertexAttribArray(1); 
     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(3 * sizeof(float))); 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[0]); // red 
    } 
    glBindVertexArray(0); 

    glBindVertexArray(p_vao[1]); 
    { 
     glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object); 
     glEnableVertexAttribArray(0); 
     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(0)); 
     glEnableVertexAttribArray(1); 
     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(3 * sizeof(float))); 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[1]); // blue 
    } 
    glBindVertexArray(0); 

#ifdef BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[2]); 
    // bind the buffer with the black quad (not inside VAO, should NOT be seen) 
#endif // BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER 

    // [compile shaders here] 

    return true; // success 
} 

上面的代碼創建包含三個四邊形,紅色,藍色和黑色的一個頂點緩衝區。然後它創建三個指向單個四邊形的索引緩衝區。然後創建和設置兩個VAO,一個應該包含紅色四邊形索引,另一個應該包含藍色四邊形索引。黑色方塊不應該渲染(假設BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER 定義)。

void onDraw() 
{ 
    glClearColor(.5f, .5f, .5f, 0); 
    glClear(GL_COLOR_BUFFER_BIT); 
    glDisable(GL_DEPTH_TEST); 

    glUseProgram(n_program_object); 

    static int n_last_color = -1; 
    int n_color = (clock()/2000) % 2; 
    if(n_last_color != n_color) { 
     printf("now drawing %s quad\n", (n_color)? "blue" : "red"); 
     n_last_color = n_color; 
    } 

    glBindVertexArray(p_vao[n_color]); 
#ifdef VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[n_color]); // fixes the problem 
#endif // VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER 
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 
    glBindVertexArray(0); 
} 

這清除視口灰色,且重複的方式呈現藍色或紅色四(它也打印哪一個)。雖然這可以在桌面GPU上工作,但它不能在筆記本GPU上工作(除非定義了VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER宏,否則將呈現黑色四元組)。取消定義BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER宏會使四元組藍色,因爲藍色索引緩衝區最後被綁定,呈現紅四不管是什麼。

所以我看到它的方式,這是無論是在我的應該怎麼維羅工作,在我的代碼中的錯誤,或者驅動程序錯誤理解一個致命的誤解。

Full source
Binaries (windows, 32 bit)

+6

您的代碼更有可能不是最初將元素緩衝區放入VAO中。你爲什麼不向我們展示你的VAO初始化代碼。 –

+7

哦,來吧,不是那麼愚蠢。另外我說它在GeForce 260/480上工作。在寫評論之前閱讀帖子。我完全有能力調試我的OpenGL代碼。這個問題是關於OpenGL實現和兼容性的區別。 –

+6

只是因爲代碼的作品並不意味着它是*正確*。代碼可以通過偶然的情況或其他方式來工作。它既失敗又成功的NVIDIA驅動程序的事實表明用戶錯誤。如果它在NVIDIA上運行並且在ATI上失敗,反之亦然,那麼它很可能是驅動程序錯誤。但NVIDIA尤其是自相似。所以如果它有時在某些NVIDIA硬件上工作,有時不會,那聽起來像是用戶錯誤。 –

回答

22

過了一段時間,我發現這實際上有點不好。配備移動NVIDIA Quadro 4200圖形卡的筆記本電腦已設置好,以便所有應用程序默認都能在英特爾圖形上運行,即使膝上型計算機處於性能模式。我不明白爲什麼有人想要這樣做,因爲那時沒有辦法讓任何應用程序使用OpenGL中功能更強大的GPU(它仍然可以用於OpenCL,因爲有明確的設備選擇,也可能用於DirectX - 這可以解釋爲什麼一些遊戲運行平穩)。儘管如此,所描述的錯誤行爲只是英特爾驅動程序中的一個錯誤,這就是它的全部。英特爾驅動程序不保存ELEMENT_ARRAY_BUFFER_BINDING。那裏。

我很抱歉問這個問題,因爲沒有辦法在不知道上面的情況下給出一個很好的答案。

+0

您是否向英特爾提交了一個錯誤?任何跟進這個故事?知道這個世界上存在的這些微妙的問題,深深地困擾着我作爲新手圖形程序員。 – Philip

+0

@飛利浦我沒有考慮過,我幾乎不考慮英特爾是一個可用的GPU,所以對我來說很容易說我不會支持英特爾GPU。但也許這是一個好主意。如果你這樣做,請在評論中發佈錯誤跟蹤器的鏈接。謝謝。 –

+7

即使它對您沒有幫助,在SO上記錄驅動程序錯誤對我也有幫助。 – Justin

17

我確實相信ARB VAO缺少元素數組緩衝區綁定(或任何其他緩衝區綁定)狀態。

信仰不是必需的;規範告訴事實。

ARB_vertex_array_object規格:

命令

void GenVertexArrays(sizei n, uint *arrays); 

返回先前未使用的頂點數組對象名稱。這些名稱僅用於GenVertexArrays而被標記爲已使用,並且使用表6.6(CLIENT_ACTIVE_TEXTURE選擇器狀態除外),6.7和6.8(除ARRAY_BUFFER_BINDING狀態除外)中列出的狀態進行初始化。

所以我們有了它:VAO包含的整個國家都是這三個表的內容,除了上述例外。

擴展是針對The OpenGL Graphics Specification version 2.1 (PDF)寫的。因此,任何頁碼,節標籤或表號都是相對於該規範引用的。

我不打算在這裏複製這三個表格。但是,如果您查看第273頁(按規格的頁數)/第287頁(按物理頁數),您將看到表6.8。而該表如下:

  • ELEMENT_ARRAY_BUFFER_BINDING

這裏沒有歧義。這些信息可能並不清楚。但毫無疑問,它有那裏有。 ELEMENT_ARRAY_BUFFER_BINDING是VAO州的一部分。

因此,您的問題可能來自兩個來源:

  1. 驅動程序的bug。正如我在評論中所述,驅動程序錯誤似乎不太可能。並非不可能,只是不太可能NVIDIA的驅動程序對於不同的硬件來說非常相似,而且VAO幾乎不會在硬件上進行鏡像。除非你使用不同版本的驅動程序,否則沒有理由期望由於驅動程序錯誤而導致錯誤。

  2. 用戶錯誤。我知道你聲稱你的代碼有效,因此沒關係。 所有人對某些代碼作出了這種聲明。有很多時候我會發誓一些代碼工作得很好。然而它被打破了;它恰好碰巧經過。它發生了。如果你發佈你的代碼,那麼至少我們可以打折這種可能性。否則,我們只有你的話。考慮到人類多麼錯誤,這並不值得。

+0

夠公平的。我不能發佈完整的代碼,但我會嘗試拿出足以重現錯誤的最小代碼。此外,ARB的事情是堅實的,國家真的好像在那裏。 –

+0

和來源代碼(在原始問題中編輯)。那麼這是驅動程序錯誤還是我的錯? –

0

可能的原因是您的英特爾適配器無法提供OpenGL 3.3上下文,而是默認爲2.1或類似的。正如其他人所指出的那樣,在早期版本的OpenGL中,元素數組緩衝區狀態不是VAO的一部分。

+1

這實際上是不可能的,因爲我創建的上下文是向前兼容的,所以它不能默認爲較低的。或者如果確實如此,那絕對不應該。 –

+0

我以爲有一個glGet調用你可以運行查詢上下文版本。瞭解。 –

+0

@StevenLu是的,有'glGetString(GL_VERSION)',但這隻意味着一個錯誤(元素數組沒有被緩存)只是由一個更糟糕的錯誤造成的(創建一箇舊的2.1環境而不是一個向前兼容的環境) 。 –

-1

我可以想象ELEMENT緩衝區沒有被緩存;如果你這樣做:

glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object); 
glEnableVertexAttribArray(0); 
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(0)); 

這好像是說:

gBoundBuffer_GL_ARRAY_BUFFER=n_vertex_buffer_object; 
currentVAO->enable|=(1<<0); 
currentVAO->vertexBuffer=IndexToPointer(gBoundBuffer_GL_ARRAY_BUFFER); 

換句話說,glBindBuffer()沒有做什麼,但設定的GL_ARRAY_BUFFER值。在你修改VAO的地方是glVertexAttribPointer()

所以,當你這樣做:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[0]); 
glBindVertexArray(0); 

你真的:

gBoundBuffer_GL_ELEMENT_ARRAY_BUFFER=p_index_buffer_object_list[0]; 
currentVAO=0; 

在有意義的GL_ELEMENT_ARRAY_BUFFER結合沒有做任何事情。 我不確定是否有類似於glVertexAttribPointer()的變體,但它(如glElementPointer())實際上會作用於VAO。

+0

那麼,你描述的行爲(元素數組沒有被緩存)實際上違背了規範,這樣做沒有什麼意義。當你想繪製幾何圖形時,只有極少數情況下頂點和索引是可分離的。大多數情況下,模型是從藝術家創建的文件加載的,並且一個模型的索引不適合來自任何其他模型的頂點。因此,至少在「圖形應用程序,顯示3D網格」場景中,同時綁定索引緩衝區和頂點attrib指針實際上是非常有意義的。 –