glDrawElements излъчва GL_INVALID_OPERATION, когато използва AMD драйвер на Linux

Имам малко код за изобразяване на OpenGL 2.1, който работи чудесно, когато използвам карта/драйвер на nVidia или AMD драйвера с отворен код, но не работи, когато използвам официалния драйвер fglrx. Той просто показва сив екран (цвят glClear) и не рисува нищо.

gDEBugger показва, че glDrawElements дава грешка GL_INVALID_OPERATION. Според тази страница (Какво може да причини glDrawArrays да генерира грешка GL_INVALID_OPERATION?) има много полудокументирани възможни причини за тази грешка. Шейдърът се компилира добре и размерът на буфера също трябва да е добър и аз не използвам геометрични шейдъри (очевидно). Това е просто извикване на изтегляне на куб, само с един атрибут на върха. Кодът е по-долу.

glUseProgram(r->program->getProgram());

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, r->texture->glID );
glUniform1i(r->program->getUniform("texture").location, GL_TEXTURE0);
glUniform4f(r->program->getUniform("colour").location, r->colour.x, r->colour.y, r->colour.z, r->colour.w);

glBindBuffer(GL_ARRAY_BUFFER, r->vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, r->ibo);

glVertexAttribPointer(
    r->program->getAttribute("position").location,  // attribute
    3,                                  // size
    GL_FLOAT,                           // type
    GL_FALSE,                           // normalized?
    sizeof(GLfloat)*3,                  // stride
    reinterpret_cast<void*>(0)          // array buffer offset
);
glEnableVertexAttribArray(r->program->getAttribute("position").location);

glUniformMatrix4fv(r->program->getUniform("modelToCameraMatrix").location, 1, GL_FALSE, glm::value_ptr(modelToCameraMatrix));

glDrawElements(
    r->mesh->mode,                  // mode
    r->mesh->nrOfInds,              // count
    GL_UNSIGNED_SHORT,              // type
    reinterpret_cast<void*>(0)      // element array buffer offset
);

Нямам представа какво се случва или какво може да причинява тази грешка. Ако някой има някакви насоки за това какво може да причинява това да се случи с драйвера fglrx, а не с който и да е друг драйвер, ще се радвам да го чуя. Ако имате нужда от още код, с радост ще го осигуря, разбира се.


person KillerSponge    schedule 18.08.2013    source източник
comment
Хм, този код ми изглежда добре (за GL 2.1). Ако по някаква причина се окажете с ›=3.x основен контекст на профила, грешката може да е резултат от липсата на VAO обвързаност. Друга възможност може да бъде, че програмата за шейдъри всъщност не успява да се свърже/компилира (драйверите на AMD са доста строги, докато особено nvidia приема доста нестандартни конструкции). Но това също би довело само до докладваното поведение в ›=3.x основен контекст, с 2.x или съвместимост, трябва да се върне към конвейер с фиксирана функция и не трябва да генерира грешка.   -  person derhass    schedule 18.08.2013


Отговори (2)


Нека анализираме това:

glUniform1i(r->program->getUniform("texture").location, GL_TEXTURE0 + i);

Спецификацията на OpenGL 2.1 гласи в раздел 2.15:

Задаването на стойност на семплера на i избира единица текстурно изображение номер i. Стойностите на i варират от нула до зависимия от изпълнението максимален поддържан брой единици текстурно изображение.

Задаването на стойността на семплер е законно само с извиквания на glUniform1i{v}.

Ако една имплементация си позволи да позволи изрази като горните, тя трябва да направи известно изваждане под кориците - т.е. трябва да картографира (GL_TEXTURE0 + i) към някаква стойност в [0, MAX_UNITS - 1]. (Забележка: MAX_UNITS е само символично тук, това не е действителна GL константа!).

Това просто е несъответстващо поведение, тъй като GL_TEXTURE0 дефинира много голяма стойност и която е далеч извън обхвата на броя налични текстурни единици на който и да е стар или модерен GPU.

Между другото, GL_TEXTURE0 не е префикс, а константа.


Основната спецификация на GL 4.4 е много по-ясна за това в раздел 7.10:

Грешка INVALID_VALUE се генерира, ако Uniform1i{v} се използва за задаване на семплер на стойност, по-малка от нула или по-голяма или равна на стойността на MAX_COMBINED_TEXTURE_IMAGE_UNITS.

Относно glActiveTexture(): Тази функция приема стойност в [GL_TEXTURE0, GL_TEXTURE0 + MAX_UNITS - 1]. Сигурно не е задължително да е така, но това е дефинираното.

Не е тайна сред общността на OpenGL, че NVIDIA често е по-снизходителна и техните драйвери понякога вземат неща, които просто не работят на друг хардуер и драйвери, защото просто не трябва да работят.

Като общо правило: никога не разчитайте на специфично за внедряване поведение. Разчитайте само на спецификацията. Ако съвместимият и правилен код на приложение не работи с конкретно изпълнение, това е грешка в изпълнението.

person thokra    schedule 21.08.2013

Мисля, че разбрах какво не е наред с горния код. Дефектната линия е

glUniform1i(r->program->getUniform("texture").location, GL_TEXTURE0 + i);

което трябва да бъде

glUniform1i(r->program->getUniform("texture").location, i);

Всъщност не мога да разбера точно защо трябва да е така (glActiveTexture има ли нужда от префикса GL_TEXTURE0) и защо само драйверът на AMD го прави глупости, така че ако някой може да разясни това, ще се радвам да го чуя. :)

person KillerSponge    schedule 19.08.2013