Состояние буфера VAO и массива элементов
Недавно я писал некоторый код OpenGL 3.3 с Vertex Array Objects (VAO) и протестировал его позже на графическом адаптере Intel, где я обнаружил, к моему разочарованию, что привязка буфера массива элементов явно не является частью состояния 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);
Визуализировал геометрию. Я думал, что это просто ошибка в реализации Intel OpenGL (потому что это четко указано в GL_ARB_vertex_array_object (и даже в GL_OES_vertex_array_object) этот элемент массива является частью сохраненного состояния), но затем это произошло на мобильном устройстве NVIDIA Quadro 4200. Это не смешно.
Это ошибка драйвера, ошибка спецификации или ошибка где-то в моем коде? Код безупречно работает на GeForce 260 и 480.
У кого-нибудь был подобный опыт?
Что также странно, так это то, что GL_EXT_direct_state_access не имеет функции для привязки буфера массива элементов к VAO (но у него есть функции для указания вершинных массивов attrib, а следовательно, и массива буфера). Производители графических процессоров завинчивают спецификации и обманывают нас, или что?
EDIT:
Первоначально я не собирался показывать исходный код, потому что считал, что в этом нет необходимости. Но, как и было предложено, вот минимальный тестовый случай, который воспроизводит проблему:
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
}
Приведенный выше код создает буфер вершин, содержащий три квадрата: красный, синий и черный. Затем он создает три буфера индекса, которые указывают на отдельные квадратики. Затем два ВАО создаются и настраиваются, один должен содержать красные квадратичные индексы, а другой-синие квадратичные индексы. Черный Квадрант не должен быть отрисован вообще (предположим, что 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 quadn", (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 (черный quad отображается, если VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER макрос определен. Если макрос BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER не определен, то он становится синим, так как синий индексный буфер привязан последним. Но он не передает Красный квадрат, несмотря ни на что.
Таким образом, как я вижу, это либо фатальное заблуждение в моем понимании того, как должен работать VAO, ошибка в моем коде или ошибка драйвера.4 ответа:
Через некоторое время я понял, что на самом деле это было отчасти моей ошибкой. Ноутбук с мобильной видеокартой NVIDIA Quadro 4200 был настроен таким образом, что все приложения будут работать на Intel graphics по умолчанию, даже когда ноутбук был в режиме производительности. Я не понимаю, почему кто-то хочет это сделать, так как тогда не было возможности для любого приложения использовать более мощный GPU от OpenGL (его все еще можно было использовать для OpenCL, поскольку есть явный выбор устройства, а также, возможно, для DirectX - это объясняет, почему некоторые игры проходили гладко).
Тем не менее, описанное ошибочное поведение-это просто ошибка в драйверах Intel, вот и все. Драйверы Intel не сохраняют ELEMENT_ARRAY_BUFFER_BINDING. Там.
Я искренне сожалею, что задал этот вопрос, поскольку не было никакого способа дать хороший ответ, не зная вышесказанного.
Вера не требуется; спекуляция сообщает факты.Я на самом деле считаю, что ARB VAO отсутствует состояние привязки буфера массива элементов (или любой другой привязки буфера).
Из спецификацииARB_vertex_array_object :
Команда
void GenVertexArrays(sizei n, uint *arrays);
Таким образом, мы имеем следующее: Все состояние, охватываемое VAOs, является содержанием этих трех таблиц, за отмеченными исключениями.Возвращает предыдущие неиспользуемые имена объектов массива вершин . Эти имена помечаются как используемые только для целей GenVertexArrays и инициализируются с помощью состояния перечислены в таблицах 6.6 (за исключением состояния селектора CLIENT_ACTIVE_TEXTURE), 6.7 и 6.8 (за исключением состояния ARRAY_BUFFER_BINDING).
Расширение написано противспецификации OpenGL Graphics version 2.1 (PDF) . Таким образом, любые номера страниц, метки разделов или номера таблиц ссылаются относительно этой спецификации.
Я ... не собираюсь копировать эти три таблицы здесь. Но если вы посмотрите на страницу 273 (по количеству страниц спецификации)/страницу 287 (по количеству физических страниц), вы найдете таблицу 6.8. И на этом столе находится следующее:
Здесь нет никакой двусмысленности. Эта информация не может быть четко изложена. Но это есть, несомненно. ЭЛЕМЕНТ_ARRAY_BUFFER_BINDING является частью состояния VAO.
- ELEMENT_ARRAY_BUFFER_BINDING
Следовательно, ваша проблема может исходить от одного из два источника:
Ошибка водителя. Как я уже сказал в комментарии, ошибка драйвера кажется маловероятной. Не невозможно, просто маловероятно. Драйверы NVIDIA довольно самоподобны для различных аппаратных средств, и VAOs почти не отражаются в аппаратных средствах. Если вы не используете разные версии драйверов, нет никаких оснований ожидать, что ошибка будет вызвана ошибкой драйвера.
Ошибка пользователя. Я знаю, вы утверждаете, что ваш код работает, и поэтому он в порядке. каждый делает это утверждение о каком-то коде. И было много случаев, когда я готов был поклясться, что какой-то код работает просто отлично. И все же она была сломана, просто так получилось, что она прошла мимо. Такое случается. Если вы опубликуете свой код, то, по крайней мере, мы сможем исключить эту возможность. В противном случае, у нас нет ничего, кроме вашего слова. И учитывая, как часто люди ошибаются в этом, это не стоит многого.
Вероятной причиной этого является то, что ваш адаптер Intel не может предоставить контекст OpenGL 3.3, а вместо этого по умолчанию принимает значение 2.1 или подобное. Как указывали другие, в более ранних версиях OpenGL состояние буфера массива элементов не было частью VAO.
Я могу представить себе, что буфер
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
. Он находится вglVertexAttribPointer()
, где вы изменяете VAO.Итак, когда вы это сделаете:
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()
), который на самом деле действовал бы на ВАО.