opengl - ¿Qué puede hacer que glDrawArrays genere un error GL_INVALID_OPERATION?
glsl shader (3)
He estado intentando escribir una implementación de GPU de dos pasos del algoritmo de Marching Cubes, similar al detallado en el primer capítulo de GPU Gems 3, usando OpenGL y GLSL. Sin embargo, la llamada a glDrawArrays
en mi primera pasada falla de forma consistente con GL_INVALID_OPERATION
.
He buscado toda la documentación que puedo encontrar y he encontrado estas condiciones bajo las cuales glDrawArrays
puede arrojar ese error:
-
GL_INVALID_OPERATION
se genera si un nombre de objeto de almacenamiento intermedio distinto de cero está vinculado a un conjunto habilitado o al enlaceGL_DRAW_INDIRECT_BUFFER
y el almacén de datos del objeto de almacenamiento en búfer está actualmente asignado. -
GL_INVALID_OPERATION
se genera siglDrawArrays
se ejecuta entre la ejecución deglBegin
y elglEnd
correspondiente. -
GL_INVALID_OPERATION
será generado porglDrawArrays
oglDrawElements
si dos muestreadores activos en el objeto de programa actual son de tipos diferentes, pero se refieren a la misma unidad de imagen de textura. -
GL_INVALID_OPERATION
se genera si un sombreado de geometría está activo y el modo es incompatible con el tipo de primitiva de entrada del sombreador de geometría en el objeto de programa actualmente instalado. -
GL_INVALID_OPERATION
se genera si el modo esGL_PATCHES
y no está activo el shader de control de teselación. -
GL_INVALID_OPERATION
se genera si registrar los vértices de una primitiva a los objetos de búfer que se utilizan para efectos de realimentación de transformación resultaría en exceder los límites del tamaño de cualquier objeto de búfer o en exceder el desplazamiento de posición final + tamaño - 1, establecido porglBindBufferRange
. -
GL_INVALID_OPERATION
es generado porglDrawArrays()
si no hay shader de geometría presente, la realimentación de transformación está activa y el modo no es uno de los modos permitidos. -
GL_INVALID_OPERATION
es generado porglDrawArrays()
si está presente un sombreado de geometría, la regeneración de transformación está activa y el tipo de primitiva de salida del sombreado de geometría no coincide con el modo primitivo de realimentación de transformación. -
GL_INVALID_OPERATION
se genera si el programa shader vinculado no es válido. - EDIT 10/10/12:
GL_INVALID_OPERATION
se genera si la regeneración de transformación está en uso, y el búfer vinculado al punto de enlace de realimentación de transformación también está vinculado al punto de enlace del búfer de la matriz. Este es el problema que estaba teniendo, debido a un error tipográfico en el que estoy vinculado. Si bien la especificación indica que esto es ilegal, no está incluido en glDrawArrays como una de las razones por las que puede arrojar un error en la documentación que encontré.
Desafortunadamente, ninguna pieza de documentación oficial que puedo encontrar cubre más de 3 de estos. Tuve que recopilar esta lista de numerosas fuentes. Los puntos 7 y 8 en realidad provienen de la documentación para glBeginTransformFeedback
, y el punto 9 no parece estar documentado en absoluto. Lo encontré mencionado en una publicación del foro en alguna parte. Sin embargo, todavía no creo que esta lista esté completa, ya que ninguno de estos parece explicar el error que estoy recibiendo.
- No estoy mapeando ningún búfer en mi programa, en ninguna parte.
- Estoy usando el perfil Core, por lo que
glBegin
yglEnd
ni siquiera están disponibles. - Tengo dos muestreadores, y son de diferentes tipos, pero definitivamente están asignados a diferentes texturas.
- Un sombreador de geometría está activo, pero su diseño de entrada es de
layout (points) in
, y se llama aGL_POINTS
conGL_POINTS
. - No estoy usando
GL_PATCHES
o sombreadores de teselación de ningún tipo. - Me he asegurado de asignar la máxima cantidad de espacio posible a mis sombreadores de geometría. Luego traté de cuadruplicarlo. No ayudó.
- Hay un sombreador de geometría Ver el siguiente punto
- La retroalimentación de transformación se está utilizando y hay un sombreador de geometría, pero el diseño de salida es de
layout (points) out
y se llama aGL_POINTS
conGL_POINTS
. - Intenté insertar una llamada a
glValidateProgram
justo antes de la llamada aglDrawArrays
, y me devolvióGL_TRUE
.
El código de OpenGL real está aquí:
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);
}
El error real es la llamada a glDrawArrays
en RenderPass1
. Merece la pena señalar que si hago un comentario de las llamadas a glBeginTransformFeedback
y glEndTransformFeedback
, entonces glDrawArrays
deja de generar el error. Entonces, lo que sea que esté mal, probablemente esté relacionado de alguna manera con la transformación de los comentarios.
Editar 18/08/12, 9 PM:
Acabo de encontrar la característica NVIDIA GLExpert en gDEBugger, con la que no estaba familiarizado previamente. Cuando lo GL_INVALID_OPERATION
, dio información un tanto más sustancial sobre GL_INVALID_OPERATION
, específicamente. The current operation is illegal in the current state: Buffer is mapped.
. Así que estoy corriendo en el punto 1, arriba. Aunque no tengo idea de cómo.
No tengo llamadas a glMapBuffer
, ni a ninguna función relacionada, en ninguna parte de mi código. Configuré gDEBugger para interrumpir cualquier llamada a glMapBuffer
, glMapBufferARB
, glMapBufferRange
, glUnmapBuffer
y glUnmapBufferARB
, y no se rompió en ningún lado. Luego agregué código al inicio de RenderPass1
para desasignar explícitamente las memorias intermedias molestas. No solo el error no desapareció, sino que las llamadas a glUnmapBuffer
ahora generan ambas. The current operation is illegal in the current state: Buffer is unbound or is already unmapped.
. Entonces, si ninguno de los búferes que estoy utilizando se asignan, ¿de dónde viene el error?
Editar 8/19/12, 12 AM:
Basado en los mensajes de error que estoy obteniendo de GLExpert en gDEBugger, parece que al llamar a glBeginTransformFeedback
está causando que el buffer enlazado a GL_TRANSFORM_FEEDBACK_BUFFER
. Específicamente, cuando hago clic en el búfer en "Textures, Buffers and Images Viewer", emite el mensaje. The current operation is illegal in the current state: Buffer must be bound and not mapped.
. Sin embargo, si agrego esto entre glBeginTransformFeedback
y glEndTransformFeedback
:
int bufferBinding;
glGetBufferParameteriv(GL_TRANSFORM_FEEDBACK_BUFFER, GL_BUFFER_MAPPED, &bufferBinding);
printf("Transform feedback buffer binding: %d/n", bufferBinding);
emite 0, lo que indicaría que GL_TRANSFORM_FEEDBACK_BUFFER
no está asignado. Si este buffer está mapeado en otro punto de enlace, ¿aún así devolvería 0? ¿Por qué glBeginTransformFeedback
correlacionaría el búfer, glBeginTransformFeedback
inutilizable para transformar comentarios?
Cuanto más aprendo aquí, más confuso me estoy volviendo.
Editar 10/10/12:
Como indiqué en mi respuesta debajo de la solución de Nicol Bolas, encontré el problema, y es el mismo que encontró: Debido a un estúpido error tipográfico, estaba vinculando el mismo buffer a los puntos de enlace de entrada y salida.
Lo encontré probablemente dos semanas después de publicar la pregunta. Había renunciado por frustración por un tiempo, y al final volví y básicamente volví a implementar todo desde cero, comparando regularmente partes y piezas del antiguo, que no funciona. Cuando terminé, la nueva versión funcionó, y fue cuando busqué las diferencias que descubrí que había estado vinculando el buffer incorrecto.
Descubrí tu problema: estás renderizando en el mismo buffer que estás obteniendo tus datos de vértice.
glBindVertexArray (vaoPass2);
Creo que vaoPass1
decir vaoPass1
De la especificación:
Los almacenamientos intermedios no deben estar vinculados o en uso para la retroalimentación de transformación y otros fines en el GL. Específicamente, si un objeto de búfer se vincula simultáneamente a un punto de enlace de búfer de realimentación de transformación y en cualquier otro lugar en el GL, cualquier escritura o lectura del búfer genera valores indefinidos. Los ejemplos de tales enlaces incluyen ReadPixels a un punto de enlace de objetos de búfer de píxeles y acceso de cliente a un búfer mapeado con MapBuffer.
Ahora, debes obtener valores indefinidos; No estoy seguro de que un error GL califique, pero probablemente debería ser un error.
Otro caso (aparentemente no documentado) donde glDrawArrays
y glDrawElements
fallan con GL_INVALID_OPERATION
:
-
GL_INVALID_OPERATION
se genera si un uniforme de muestra se establece en un identificador de unidad de textura no válido. (He realizado erróneamenteglUniform1i(location, GL_TEXTURE0);
cuando quise decirglUniform1i(location, 0);
)
Otro caso (no documentado) donde las glDraw*()
pueden fallar con GL_INVALID_OPERATION
:
-
GL_INVALID_OPERATION
se genera si un uniforme de muestra se establece en una unidad de textura ligada a una textura del tipo incorrecto. Por ejemplo, si se establece ununiform sampler2D
glUniform1i(location, 0);
, peroGL_TEXTURE0
tiene una texturaGL_TEXTURE_2D_ARRAY
vinculada.