Хобрук: Ваш путь к мастерству в программировании

Как полностью отвязать GL_ELEMENT_ARRAY_BUFFER?

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

Мне удалось немного сузить круг — если я сделаю одно изменение, течь перестанет, — но я не могу понять, почему.

В каждом из 4 потоков (каждый со своим общим контекстом) я делаю следующее в каждом цикле рендеринга:

// Upload

positionBuffer = getUnusedArrayBufferFromPool();
glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*vertexCount, positions, GL_STREAM_DRAW);

{
    GLuint vertexArray;
    glGenVertexArrays(1, &vertexArray);
    glBindVertexArray(vertexArray);

    elementBuffer = getUnusedElementArrayBufferFromPool();
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*elementCount, elements, GL_STREAM_DRAW);

    glBindVertexArray(0);
    glDeleteVertexArrays(1, &vertexArray);
}

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);



// Render (possibly on a different context)

GLuint vertexArray;
glGenVertexArrays(1, &vertexArray);
glBindVertexArray(vertexArray);

glUseProgram(programName);
{
    GLint positionAttribute = glGetAttribLocation(programName, "position");
    glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
    glVertexAttribPointer((GLuint)positionAttribute, 4 /* XYZW */, GL_FLOAT, GL_FALSE, sizeof(float)*4, (void*)0);
    glEnableVertexAttribArray((GLuint)positionAttribute);

    {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
        glDrawElements(GL_TRIANGLES, (GLsizei)elementCount, GL_UNSIGNED_INT, (void*)0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    }

    glDisableVertexAttribArray((GLuint)positionAttribute);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}
glUseProgram(0);

glBindVertexArray(0);
glDeleteVertexArrays(1, &vertexArray);



// Cleanup (possibly on a different context)

glBindBuffer(GL_ARRAY_BUFFER, positionBuffer);
glBufferData(GL_ARRAY_BUFFER, 0, 0, GL_STREAM_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
throwArrayBufferBackInPool(positionBuffer);

//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
//glBufferData(GL_ELEMENT_ARRAY_BUFFER, 0, 0, GL_STREAM_DRAW);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
//throwElementArrayBufferBackInPool(elementBuffer); // Why does this leak if we recycle it?
glDeleteBuffers(1, &elementBuffer);

Если я поменяю местами последние 2 строки — если я верну буфер элемента обратно в пул вместо того, чтобы удалять его в каждом цикле рендеринга — OpenGL Driver Monitor укажет на огромную утечку.

Но я бы предпочел объединить его, так как вызов glDeleteBuffers() в каждом кадре очень медленный.

Что мне не хватает? Я предполагаю, что мне не удается отвязать elementBuffer от чего-то — и что-то удерживает ссылку на него, вызывая утечку — но я не могу понять, что.


Изменить: только что протестировано на другой системе (Mac OS 10.6) — в этой системе происходит утечка, если я перезапускаю какие-либо буферы.

Изменить: я изменил свое приложение, чтобы GL_ARRAY_BUFFER объединялось отдельно от GL_ELEMENT_ARRAY_BUFFER, а elementBuffer последовательно связывалось с GL_ELEMENT_ARRAY_BUFFER. Но все равно протекает, если я throwElementArrayBufferBackInPool(elementBuffer);.

Редактировать. Объяснено, почему я создаю и удаляю VAO во время загрузки и во время рендеринга — потому что они могут происходить в разных общих контекстах GL, а VAO не могут быть разделены между контекстами.

Редактировать: я модифицировал свое приложение, чтобы оно предоставляло буферные данные нулевого размера, прежде чем возвращать буферы обратно в пул, но утечка по-прежнему происходит так же быстро.


  • Есть ли шанс, что вы могли бы объяснить/показать, как реализован throwElementArrayBufferBackInPool (...)? VAO являются объектами-контейнерами, если вы удаляете объект буфера, привязанный к одному из них (на самом деле единственная привязка объекта буфера, которую отслеживает VAO, — это буфер элемента, указатели вершин работают по-другому), в то время как сам этот объект VAO не активен, то буферный объект не удаляется полностью до тех пор, пока не будет удалена ссылка на него со стороны VAO. Если VAO активен, когда вы удаляете буфер элементов, то он автоматически освобождается. Это может иметь значение в данной ситуации, но мне нужно увидеть реализацию. 29.11.2013
  • Вероятно, вы могли бы решить все это, вызвав glBufferData (...) с 0 для размера и сохранив пул зарезервированных, но пустых имен буферов для повторного использования вместо удаления объектов буфера. Это освободит хранилище данных немедленно (условно говоря), не дожидаясь удаления всех ссылок во всех контекстах и ​​объектах-контейнерах. 29.11.2013
  • @ AndonM.Coleman: Пул - это просто простая поточно-ориентированная оболочка для C++ std::vector. Я только что разместил здесь отрывок из исходного кода: gist.github.com/smokris/7711889 30.11.2013
  • @AndonM.Coleman: Спасибо. Я модифицировал свое приложение, чтобы оно предоставляло буферные данные нулевого размера, прежде чем возвращать буферы обратно в пул (см. измененный исходный код выше), но оно по-прежнему протекает так же быстро. 30.11.2013
  • Ну, я не предполагал, что это решение для избавления от оборванных ссылок на буферы массива элементов. Однако он освобождает любую память, связанную с этими буферами (после завершения любой незавершенной операции GL, которая их использует). Таким образом, даже если у вас есть плавающие имена, которые фактически потеряны в пустоте, у вас не будет потраченного впустую хранилища графического процессора. Это не идеальное решение, но оно решит некоторые проблемы, пока вы будете искать настоящего виновника. В моей собственной работе у меня есть VAO и другие объекты OpenGL, полностью обернутые — движок скрывает все их состояния для облегчения отладки. 30.11.2013
  • @AndonM.Coleman: О! Конечно, это не решило бы проблему с оборванными ссылками. Не знаю, о чем я думал. Спасибо за разъяснения. 03.12.2013

Ответы:


1

Ваш буферный пул используется для привязки GL_ARRAY_BUFFER, а также объектов GL_ELEMENT_ARRAY_BUFFER. Хуже того, вы сначала используете elementBuffer для привязки GL_ARRAY_BUFFER, а затем продолжаете использовать его для GL_ELEMENT_ARRAY_BUFFER. Я не полностью обдумал это, но где-то между смешиванием пространств имен и непоследовательным использованием привязок я бы поставил на это свои деньги.

Мое предложение: создайте отдельный пул имен буферов для GL_ELEMENT_ARRAY_BUFFER использования и убедитесь, что вы последовательно используете его только для этого.

28.11.2013
  • Я изменил свое приложение (и приведенный выше пример кода), чтобы GL_ARRAY_BUFFER объединялся отдельно от GL_ELEMENT_ARRAY_BUFFER, а elementBuffer последовательно связывался с GL_ELEMENT_ARRAY_BUFFER. Но все равно протекает, если я throwElementArrayBufferBackInPool(elementBuffer);. 28.11.2013
  • Новые материалы

    Понимание дженериков в TypeScript: подробное руководство
    Введение TypeScript, строго типизированный надмножество JavaScript, хорошо известен своей способностью улучшать масштабируемость, удобочитаемость и ремонтопригодность приложений. Одной из..

    Учебные заметки JavaScript Object Oriented Labs
    Вот моя седьмая неделя обучения программированию. После ruby ​​и его фреймворка rails я начал изучать самый популярный язык интерфейса — javascript. В отличие от ruby, javascript — это более..

    Разбор строк запроса в vue.js
    Иногда вам нужно получить данные из строк запроса, в этой статье показано, как это сделать. В жизни каждого дизайнера/разработчика наступает момент, когда им необходимо беспрепятственно..

    Предсказание моей следующей любимой книги 📚 Благодаря данным Goodreads и машинному обучению 👨‍💻
    «Если вы не любите читать, значит, вы не нашли нужную книгу». - J.K. Роулинг Эта статья сильно отличается от тех, к которым вы, возможно, привыкли . Мне очень понравилось поработать над..

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..