Подтвердить что ты не робот

Состояние буфера VAO и элемента

Недавно я написал код OpenGL 3.3 с объектами Vertex Array (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);

отображает геометрию. Я думал, что это была простая ошибка в реализации OpenGL в Intel (потому что в GL_ARB_vertex_array_object (и даже в GL_OES_vertex_array_object) четко указано, что элементный массив является частью сохраненного состояния), но затем он произошел на мобильном NVIDIA Quadro 4200. Это не весело.

Это ошибка драйвера, ошибка спецификации или ошибка где-то в моем коде? Код работает безупречно на GeForce 260 и 480.

У кого-то был подобный опыт?

Также странно, что GL_EXT_direct_state_access не имеет функции привязки буфера массива элементов к VAO (но он имеет функции для указания массивов атрибутов вершин и, следовательно, буферов массива). Являются ли производители графических процессоров завинчиванием спецификаций и обмана на нас, или что?

ИЗМЕНИТЬ

Я изначально не собирался показывать какой-либо исходный код, потому что считал, что здесь не нужно. Но, как просили, вот минимальный тестовый пример, который воспроизводит проблему:

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);
}

Это очистит окно просмотра до серого и повторяет синий или красный квадрат (он также печатает один). Хотя это работает на настольном графическом процессоре, оно не работает на графическом процессоре ноутбука (черный квадрат отображается, если не указан макрос VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER. Невозможно определить макрос BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER, что делает синий цвет синим, поскольку синий буфер буфера привязан последним. сделайте красный квадрат, несмотря ни на что.

Итак, как я его вижу, это либо фатальное заблуждение в моем понимании того, как должно работать VAO, ошибка в моем коде или ошибка драйвера.

Полный источник
Binaries (окна, 32 бит)

4b9b3361

Ответ 1

Через некоторое время я узнал, что это на самом деле отчасти плохо. Ноутбук с мобильной графикой NVIDIA Quadro 4200 был установлен таким образом, чтобы все приложения работали на графике Intel по умолчанию, даже когда ноутбук находился в режиме производительности. Я не понимаю, почему кто-то захочет это сделать, поскольку тогда никому не было никакого способа использовать более мощный графический процессор OpenGL (все еще можно было использовать его для OpenCL, поскольку есть явный выбор устройства, а также, возможно, для DirectX - это объясняет, почему некоторые игры прошли гладко).

Тем не менее, описанное ошибочное поведение - всего лишь ошибка в драйверах Intel, что все там. Драйверы Intel не сохраняют ELEMENT_ARRAY_BUFFER_BINDING. Там.

Я искренне сожалею о том, что задал вопрос, поскольку не было никакого способа дать хороший ответ, не зная выше.

Ответ 2

Я действительно считаю, что 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, представляет собой содержимое этих трех таблиц с отмеченными исключениями.

Расширение написано с помощью Спецификация графики OpenGL версии 2.1 (PDF). Поэтому к номерам ссылок, разделам или номерам таблиц относятся ссылки на эту спецификацию.

Я не собираюсь копировать эти три таблицы здесь. Но если вы посмотрите на стр. 273 (по количеству страниц спецификации)/стр. 287 (по количеству физических страниц), вы найдете таблицу 6.8. И на этой таблице следующее:

  • ELEMENT_ARRAY_BUFFER_BINDING

Здесь нет никакой двусмысленности. Информация может быть явно не указана. Но это там, бесспорно. ELEMENT_ARRAY_BUFFER_BINDING является частью состояния VAO.

Следовательно, ваша проблема может исходить из одного из двух источников:

  • Ошибка драйвера. Как я уже сказал в комментарии, ошибка драйвера кажется маловероятной. Не невозможно, просто маловероятно. Драйверы NVIDIA довольно автомодельны для разных аппаратных средств, а VAO вряд ли отражены в аппаратных средствах. Если вы не используете разные версии драйверов, есть мало оснований ожидать, что ошибка связана с ошибкой драйвера.

  • Ошибка пользователя. Я знаю, что вы утверждаете, что ваш код работает, и поэтому все в порядке. Все заявляют о некотором кодексе. И было много раз, когда я поклялся, что какой-то код работает нормально. Но это было сломано; просто так случилось. Бывает. Если вы разместите свой код, то, по крайней мере, мы сможем отказаться от этой возможности. В противном случае у нас есть не более чем ваше слово. И учитывая, как часто люди ошибаются в этом, это не стоит того.

Ответ 3

Вероятная причина этого заключается в том, что ваш адаптер Intel не может предоставить контекст OpenGL 3.3, но по умолчанию используется значение 2.1 или тому подобное. Как указывали другие, в более ранних версиях OpenGL состояние буфера элемента массива не было частью VAO.

Ответ 4

Я могу представить, что буфер 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()), который фактически действовал бы на VAO.