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

Что может заставить glDrawArrays генерировать ошибку GL_INVALID_OPERATION?

Я пытаюсь написать двухпроходную графическую реализацию алгоритма Marching Cubes, аналогичную той, что описана в первой главе 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 генерируется, если запись вершин примитива в буферные объекты, используемые для целей обратной связи преобразования, приведет либо к превышению пределов любого размера буферных объектов, либо к превышению смещения конечной позиции + размер - 1, как установлено glBindBufferRange.
  • GL_INVALID_OPERATION генерируется glDrawArrays(), если геодезический шейдер отсутствует, преобразование обратной связи активно и режим не является одним из разрешенных режимов.
  • GL_INVALID_OPERATION генерируется glDrawArrays(), если присутствует геометрический шейдер, преобразование обратной связи является активным, а выходной примитивный тип геометрического шейдера не соответствует примитиву преобразования обратного преобразования.
  • GL_INVALID_OPERATION генерируется, если неверная программа шейдера недействительна.
  • EDIT 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 прекратит генерировать ошибку. Так что, что бы ни случилось, это, вероятно, каким-то образом связано с преобразованием обратной связи.

Редактировать 8/18/12, 9 вечера:

Я только что нашел функцию 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.. Итак, если ни один из буферов, которые я использую, не отображается, откуда происходит ошибка?

Редактировать 8/19/12, 12:00:

На основании сообщений об ошибках, которые я получаю из GLExpert в gDEBugger, похоже, что вызов glBeginTransformFeedback вызывает привязку буфера, привязанного к GL_TRANSFORM_FEEDBACK_BUFFER. В частности, когда я нажимаю на буфер в "Textures, Buffers and Images Viewer", он выдает сообщение 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:

Как было указано в моем ответе ниже на решение Nicol Bolas, я нашел проблему, и он тот же, что и он. Из-за глупой опечатки я связывал один и тот же буфер как с входными, так и с выходными точками привязки.

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

4b9b3361

Ответ 1

Я понял вашу проблему: вы передаете тот же буфер, что и источники данных вершин.

glBindVertexArray (vaoPass2);

Я думаю, вы имели в виду vaoPass1

Из спецификации:

Буферы не должны быть связаны или использоваться как для обратной связи преобразования, так и для других цели в GL. В частности, если буферный объект одновременно связан с преобразовать точку привязки буфера обратной связи и в другое место в GL, любая запись в или считывает из буфера генерирует значения undefined. Примеры таких привязок включают ReadPixels в точку привязки объекта пиксельного буфера и клиентский доступ к буфер, сопоставленный с MapBuffer.

Теперь вы должны получить значения undefined; Я не уверен, что ошибка GL соответствует, но, вероятно, это должна быть ошибка.

Ответ 2

Другой (видимо, недокументированный) случай, когда glDrawArrays и glDrawElements терпят неудачу с GL_INVALID_OPERATION:

  • GL_INVALID_OPERATION генерируется, если униформа сэмплера установлена ​​на недопустимый идентификатор текстурной единицы. (Я ошибочно выполнил glUniform1i(location, GL_TEXTURE0);, когда я имел в виду glUniform1i(location, 0);.)

Ответ 3

Другой (недокументированный) случай, когда вызовы glDraw*() могут сбой с GL_INVALID_OPERATION:

  • GL_INVALID_OPERATION генерируется, если униформа сэмплера установлена ​​в блок текстуры, привязанный к текстуре неправильного типа. Например, если a uniform sampler2D установлен glUniform1i(location, 0);, но GL_TEXTURE0 имеет GL_TEXTURE_2D_ARRAY привязку текстуры.