Что может заставить glDrawArrays генерировать ошибку GL_INVALID_OPERATION?
Я пытался написать двухпроходную реализацию алгоритма Marching Cubes для GPU, аналогичную описанной в первой главе GPU Gems 3, с использованием OpenGL и GLSL. Тем не менее, призыв к glDrawArrays
в моем первом проходе последовательно терпит неудачу с GL_INVALID_OPERATION
,
Я посмотрел всю документацию, которую я могу найти, и нашел эти условия, при которых glDrawArrays
может сгенерировать эту ошибку:
GL_INVALID_OPERATION
генерируется, если ненулевое имя объекта буфера связано с включенным массивом или сGL_DRAW_INDIRECT_BUFFER
привязка и хранилище данных объекта буфера в настоящее время отображается.GL_INVALID_OPERATION
генерируется, еслиglDrawArrays
выполняется между исполнениемglBegin
и соответствующийglEnd
,GL_INVALID_OPERATION
будет сгенерированоglDrawArrays
или жеglDrawElements
если любые два активных сэмплера в текущем программном объекте относятся к разным типам, но ссылаются на одну и ту же текстурную единицу изображения.GL_INVALID_OPERATION
генерируется, если геометрический шейдер активен, а режим несовместим с типом входного примитива геометрического шейдера в текущем установленном программном объекте.GL_INVALID_OPERATION
генерируется, если режимGL_PATCHES
и ни один шейдер управления тесселяцией не активен.GL_INVALID_OPERATION
генерируется, если запись вершин примитива в буферные объекты, используемые для целей обратной связи преобразования, приведет либо к превышению ограничений размера любого буферного объекта, либо к превышению конечной позиции offset + size - 1, как установленоglBindBufferRange
,GL_INVALID_OPERATION
генерируетсяglDrawArrays()
если геометрический шейдер отсутствует, обратная связь преобразования активна, и режим не является одним из разрешенных режимов.GL_INVALID_OPERATION
генерируетсяglDrawArrays()
если присутствует геометрический шейдер, обратная связь преобразования активна, и тип выходного примитива геометрического шейдера не соответствует примитиву обратной связи преобразования.GL_INVALID_OPERATION
генерируется, если связанная шейдерная программа недействительна.- РЕДАКТИРОВАТЬ 10/10/12:
GL_INVALID_OPERATION
генерируется, если используется обратная связь преобразования, и буфер, связанный с точкой привязки обратной связи преобразования, также связан с точкой привязки массива буфера. Это проблема, которая у меня возникла из-за опечатки, в которой я связался с буфером. Хотя спецификация заявляет, что это незаконно, она не указана в glDrawArrays как одна из причин, по которой она может вызвать ошибку, в любой найденной мной документации.
К сожалению, ни одна часть официальной документации, которую я могу найти, не охватывает более трех из них. Я должен был собрать этот список из многочисленных источников. Пункты 7 и 8 фактически взяты из документации для glBeginTransformFeedback
и пункт 9, похоже, совсем не задокументирован. Я нашел это упомянутое в сообщении форума где-то. Тем не менее, я все еще не думаю, что этот список полон, поскольку ни один из них, кажется, не объясняет ошибку, которую я получаю.
- Я нигде не отображаю никаких буферов в моей программе.
- Я использую профиль Core, поэтому
glBegin
а такжеglEnd
даже не доступны. - У меня есть два сэмплера, и они разных типов, но они определенно сопоставлены с разными текстурами.
- Геометрический шейдер активен, но его входной макет
layout (points) in
, а такжеglDrawArrays
вызывается сGL_POINTS
, - Я не пользуюсь
GL_PATCHES
или тесселяционные шейдеры любого рода. - Я удостоверился, что выделяю максимальное количество места, которое мои геометрические шейдеры могли бы вывести. Затем я попытался увеличить его в четыре раза. Не помогло
- Есть геометрический шейдер. Смотрите следующий пункт.
- Используется обратная связь преобразования, и есть геометрический шейдер, но выходной макет
layout (points) out
а такжеglBeginTransformFeedback
называется сGL_POINTS
, - Я пытался вставить вызов
glValidateProgram
прямо перед звонкомglDrawArrays
и вернулсяGL_TRUE
,
Фактический код OpenGL здесь:
const int SECTOR_SIZE = 32;
const int SECTOR_SIZE_CUBED = SECTOR_SIZE * SECTOR_SIZE * SECTOR_SIZE;
const int CACHE_SIZE = SECTOR_SIZE + 3;
const int CACHE_SIZE_CUBED = CACHE_SIZE * CACHE_SIZE * CACHE_SIZE;
MarchingCubesDoublePass::MarchingCubesDoublePass(ServiceProvider* svc, DensityMap* sourceData) {
this->sourceData = sourceData;
densityCache = new float[CACHE_SIZE_CUBED];
}
MarchingCubesDoublePass::~MarchingCubesDoublePass() {
delete densityCache;
}
void MarchingCubesDoublePass::InitShaders() {
ShaderInfo vertShader, geoShader, fragShader;
vertShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass1.vert", GL_VERTEX_SHADER);
svc->shader->Compile(vertShader);
geoShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass1.geo", GL_GEOMETRY_SHADER);
svc->shader->Compile(geoShader);
shaderPass1 = glCreateProgram();
static const char* outputVaryings[] = { "triangle" };
glTransformFeedbackVaryings(shaderPass1, 1, outputVaryings, GL_SEPARATE_ATTRIBS);
assert(svc->shader->Link(shaderPass1, vertShader, geoShader));
uniPass1DensityMap = glGetUniformLocation(shaderPass1, "densityMap");
uniPass1TriTable = glGetUniformLocation(shaderPass1, "triangleTable");
uniPass1Size = glGetUniformLocation(shaderPass1, "size");
attribPass1VertPosition = glGetAttribLocation(shaderPass1, "vertPosition");
vertShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass2.vert", GL_VERTEX_SHADER);
svc->shader->Compile(vertShader);
geoShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass2.geo", GL_GEOMETRY_SHADER);
svc->shader->Compile(geoShader);
fragShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass2.frag", GL_FRAGMENT_SHADER);
svc->shader->Compile(fragShader);
shaderPass2 = glCreateProgram();
assert(svc->shader->Link(shaderPass2, vertShader, geoShader, fragShader));
uniPass2DensityMap = glGetUniformLocation(shaderPass2, "densityMap");
uniPass2Size = glGetUniformLocation(shaderPass2, "size");
uniPass2Offset = glGetUniformLocation(shaderPass2, "offset");
uniPass2Matrix = glGetUniformLocation(shaderPass2, "matrix");
attribPass2Triangle = glGetAttribLocation(shaderPass2, "triangle");
}
void MarchingCubesDoublePass::InitTextures() {
for (int x = 0; x < CACHE_SIZE; x++) {
for (int y = 0; y < CACHE_SIZE; y++) {
for (int z = 0; z < CACHE_SIZE; z++) {
densityCache[x + y*CACHE_SIZE + z*CACHE_SIZE*CACHE_SIZE] = sourceData->GetDensity(Vector3(x-1, y-1, z-1));
}
}
}
glGenTextures(1, &densityTex);
glBindTexture(GL_TEXTURE_3D, densityTex);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexImage3D(GL_TEXTURE_3D, 0, GL_R32F, CACHE_SIZE, CACHE_SIZE, CACHE_SIZE, 0, GL_RED, GL_FLOAT, densityCache);
glGenTextures(1, &triTableTex);
glBindTexture(GL_TEXTURE_RECTANGLE, triTableTex);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_R16I, 16, 256, 0, GL_RED_INTEGER, GL_INT, triTable);
}
void MarchingCubesDoublePass::InitBuffers() {
float* voxelGrid = new float[SECTOR_SIZE_CUBED*3];
unsigned int index = 0;
for (int x = 0; x < SECTOR_SIZE; x++) {
for (int y = 0; y < SECTOR_SIZE; y++) {
for (int z = 0; z < SECTOR_SIZE; z++) {
voxelGrid[index*3 + 0] = x;
voxelGrid[index*3 + 1] = y;
voxelGrid[index*3 + 2] = z;
index++;
}
}
}
glGenBuffers(1, &bufferPass1);
glBindBuffer(GL_ARRAY_BUFFER, bufferPass1);
glBufferData(GL_ARRAY_BUFFER, SECTOR_SIZE_CUBED*3*sizeof(float), voxelGrid, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &bufferPass2);
glBindBuffer(GL_ARRAY_BUFFER, bufferPass2);
glBufferData(GL_ARRAY_BUFFER, SECTOR_SIZE_CUBED*5*sizeof(int), NULL, GL_DYNAMIC_COPY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenVertexArrays(1, &vaoPass1);
glBindVertexArray(vaoPass1);
glBindBuffer(GL_ARRAY_BUFFER, bufferPass1);
glVertexAttribPointer(attribPass1VertPosition, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(attribPass1VertPosition);
glBindVertexArray(0);
glGenVertexArrays(1, &vaoPass2);
glBindVertexArray(vaoPass2);
glBindBuffer(GL_ARRAY_BUFFER, bufferPass2);
glVertexAttribIPointer(attribPass2Triangle, 1, GL_INT, 0, (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(attribPass2Triangle);
glBindVertexArray(0);
glGenQueries(1, &queryNumTriangles);
}
void MarchingCubesDoublePass::Register(Genesis::ServiceProvider* svc, Genesis::Entity* ent) {
this->svc = svc;
this->ent = ent;
svc->scene->RegisterEntity(ent);
InitShaders();
InitTextures();
InitBuffers();
}
void MarchingCubesDoublePass::Unregister() {
if (!ent->GetBehavior<Genesis::Render>()) {
svc->scene->UnregisterEntity(ent);
}
}
void MarchingCubesDoublePass::RenderPass1() {
glEnable(GL_RASTERIZER_DISCARD);
glUseProgram(shaderPass1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, densityTex);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_RECTANGLE, triTableTex);
glUniform1i(uniPass1DensityMap, 0);
glUniform1i(uniPass1TriTable, 1);
glUniform1i(uniPass1Size, SECTOR_SIZE);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, bufferPass2);
glBindVertexArray(vaoPass2);
glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, queryNumTriangles);
glBeginTransformFeedback(GL_POINTS);
GLenum error = glGetError();
glDrawArrays(GL_POINTS, 0, SECTOR_SIZE_CUBED);
error = glGetError();
glEndTransformFeedback();
glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
glBindVertexArray(0);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
glUseProgram(0);
glDisable(GL_RASTERIZER_DISCARD);
glGetQueryObjectuiv(queryNumTriangles, GL_QUERY_RESULT, &numTriangles);
}
void MarchingCubesDoublePass::RenderPass2(Matrix mat) {
glUseProgram(shaderPass2);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, densityTex);
glUniform1i(uniPass2DensityMap, 0);
glUniform1i(uniPass2Size, SECTOR_SIZE);
glUniform3f(uniPass2Offset, 0, 0, 0);
mat.UniformMatrix(uniPass2Matrix);
glBindVertexArray(vaoPass2);
glDrawArrays(GL_POINTS, 0, numTriangles);
glBindVertexArray(0);
glUseProgram(0);
}
void MarchingCubesDoublePass::OnRender(Matrix mat) {
RenderPass1();
RenderPass2(mat);
}
Фактическая ошибка - вызов glDrawArrays
в RenderPass1
, Стоит отметить, что если я закомментирую звонки glBeginTransformFeedback
а также glEndTransformFeedback
, затем glDrawArrays
перестает генерировать ошибку. Так что, что бы ни случилось, это, вероятно, как-то связано с обратной связью.
Изменить 18.08.12, 21:00:
Я только что нашел функцию NVIDIA GLExpert в gDEBugger, с которой ранее не был знаком. Когда я включил это, он дал несколько более существенную информацию о GL_INVALID_OPERATION
конкретно The current operation is illegal in the current state: Buffer is mapped.
, Итак, я сталкиваюсь с пунктом 1 выше. Хотя понятия не имею как.
У меня нет звонков glMapBuffer
или любую связанную функцию, где-нибудь в моем коде. Я установил gDEBugger на любые вызовы glMapBuffer
, glMapBufferARB
, glMapBufferRange
, glUnmapBuffer
а также glUnmapBufferARB
и это нигде не сломалось. Затем я добавил код в начало RenderPass1
явно убрать мешающие буферы. Не только ошибка не ушла, но и звонки glUnmapBuffer
теперь оба генерируют The current operation is illegal in the current state: Buffer is unbound or is already unmapped.
, Так что, если ни один из буферов, которые я использую, не сопоставлен, откуда берется ошибка?
Изменить 19.08.12, 12:00:
Судя по сообщениям об ошибках, которые я получаю из GLExpert в gDEBugger, кажется, что вызов glBeginTransformFeedback
вызывает буфер, связанный с GL_TRANSFORM_FEEDBACK_BUFFER
стать на карту. В частности, когда я нажимаю на буфер в "Просмотр текстур, буферов и изображений", он выводит сообщение The current operation is illegal in the current state: Buffer must be bound and not mapped.
, Однако, если я добавлю это между glBeginTransformFeedback
а также glEndTransformFeedback
:
int bufferBinding;
glGetBufferParameteriv(GL_TRANSFORM_FEEDBACK_BUFFER, GL_BUFFER_MAPPED, &bufferBinding);
printf("Transform feedback buffer binding: %d\n", bufferBinding);
он выводит 0, что будет означать, что GL_TRANSFORM_FEEDBACK_BUFFER
не отображается. Если этот буфер сопоставлен с другой точкой привязки, будет ли он возвращать 0? Почему бы glBeginTransformFeedback
отобразить буфер, таким образом делая его непригодным для обратной связи преобразования?
Чем больше я учусь здесь, тем больше смущаюсь.
Изменить 10/10/12:
Как указано в моем ответе ниже на решение Николя Боласа, я нашел проблему, и он нашел ту же самую: из-за глупой опечатки я связал один и тот же буфер с входными и выходными точками привязки.
Я нашел это, вероятно, через две недели после публикации вопроса. На какое-то время я разочаровался, а потом вернулся и, по сути, заново реализовал все это с нуля, регулярно сравнивая фрагменты старого, нерабочего. Когда я закончил, новая версия работала, и когда я искал различия, которые я обнаружил, я связывал неправильный буфер.
3 ответа
Я выяснил вашу проблему: вы выполняете рендеринг в том же буфере, что и источники данных вершин.
glBindVertexArray (vaoPass2);
Я думаю ты имел ввиду vaoPass1
Из спецификации:
Буферы не должны быть связаны или использоваться как для обратной связи преобразования, так и для других целей в GL. В частности, если буферный объект одновременно связан с точкой привязки буфера обратной связи преобразования и где-либо еще в GL, любые операции записи или чтения из буфера генерируют неопределенные значения. Примеры таких привязок включают ReadPixels к точке привязки объекта пиксельного буфера и клиентский доступ к буферу, сопоставленному с MapBuffer.
Теперь вы должны получить неопределенные значения; Я не уверен, что ошибка GL имеет место, но, вероятно, это должна быть ошибка.
Еще один (явно недокументированный) случай, когда glDrawArrays
а также glDrawElements
потерпеть неудачу с GL_INVALID_OPERATION
:
GL_INVALID_OPERATION
генерируется, если для формы сэмплера задан недопустимый идентификатор единицы текстуры. (Я ошибочно выполнилglUniform1i(location, GL_TEXTURE0);
когда я имел в видуglUniform1i(location, 0);
.)
Еще один (недокументированный) случай, когда glDraw*()
звонки могут не работать с GL_INVALID_OPERATION
:
GL_INVALID_OPERATION
генерируется, если для сэмплера используется единица текстуры, связанная с текстурой неправильного типа. Например, еслиuniform sampler2D
установленоglUniform1i(location, 0);
, ноGL_TEXTURE0
имеетGL_TEXTURE_2D_ARRAY
Текстура связана.