В OpenGL, что является хорошей целью для количества вершин в VBO при сохранении хорошей частоты кадров - PullRequest
0 голосов
/ 08 апреля 2019

Я работаю над созданием 2D игрового движка с нуля, в основном для развлечения.В последнее время я действительно беспокоюсь о производительности всего двигателя.Я продолжаю читать статьи о целевом количестве полигонов, чтобы попытаться достичь, и я видел разговоры в миллионах, в то время как мне удалось получить только 40 000 без ужасных падений частоты кадров.

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

Формат, который я используюимеет 28 байтовых вершин, (три числа с плавающей точкой для положения, 2 числа с плавающей точкой для координат текстуры, 1 для цвета и 1 для буфера текстуры, из которого нужно считывать).Я думал об урезании этого, но опять же это не стоит того.

Просмотр моего кода почти 98% времени тратится на выделение, заполнение и передачу VAO на видеокарту.Так что в настоящее время это мое единственное узкое место.

Все спрайты - это только 4-сторонние многоугольники, и я просто использую GL_QUADS для рендеринга всего объекта.40000 спрайтов чувствует себя очень низко.У меня есть только один дро-колл для них, поэтому я ожидал, по крайней мере, в 10 раз больше того, что я прочитал.Я имею в виду, что некоторые модели содержат почти 40 000 многоугольников только для 3D!

Вот некоторый соответствующий код для того, как я все это отрисовываю:

//This is the main render loop, currently it's only called once per frame
    for (int i = 0; i < l_Layers.size(); i++) {
        glUseProgram(l_Layers[i]->getShader().getShaderProgram());

        GLint loc = glGetUniformLocation(l_Layers[i]->getShader().getShaderProgram(), "MVT");
        glUniformMatrix4fv(loc,1, GL_FALSE, mat.data);

        l_Layers[i]->getVertexBuffer().Bind();
        glDrawArrays(GL_QUADS, 0, l_Layers[i]->getVertexBuffer().getSize());
        l_Layers[i]->getVertexBuffer().Unbind();
    }
//These lines of code take up by far the most compute time
void OP::VertexBuffer::startBuffer(int size)
{
    flush();
    Vertices = new Vertex[size * 4];
}

void OP::VertexBuffer::submit(Vertex vertex)
{
    Vertices[Index] = vertex;
    Index++;
}
void Layer::Render() {
    l_VertexBuffer.startBuffer(l_Sprites.size());
    for (size_t i = 0; i < l_Sprites.size(); i++) {
        Vertex* vert = l_Sprites[i]->getVertexArray();
        l_VertexBuffer.submit(vert[0]);
        l_VertexBuffer.submit(vert[1]);
        l_VertexBuffer.submit(vert[2]);
        l_VertexBuffer.submit(vert[3]);
    }
}

Не знаюЯ знаю, что я делаю неправильно, но я просто не понимаю, как люди получают на порядок больше полигонов на экране.Особенно, когда у них гораздо более сложные модели, чем у меня с GL_QUADS.

1 Ответ

1 голос
/ 09 апреля 2019

98% времени тратится на выделение, заполнение и передачу VAO на видеокарту. Так что в настоящее время это мое единственное узкое место.

Создание VAO и его заполнение на самом деле должно происходить только один раз и, следовательно, не должно влиять на частоту кадров, вам нужно только связать VAO перед вызовом render.

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

Меня не удивляет, что вы проводите здесь все свое время:

//These lines of code take up by far the most compute time
void OP::VertexBuffer::startBuffer(int size)
{
    flush();
    Vertices = new Vertex[size * 4];
}

Вызов new при каждом вызове рендеринга для большого массива значительно повлияет на вашу производительность, вы также тратите время на присвоение этому массиву каждого кадра.

Кроме того, у вас, похоже, утечка памяти.

Каждый раз, когда вы звоните:

Vertices = new Vertex[size * 4];

Вы не можете освободить массив, выделенный во время предыдущего вызова Render. То, что вы делаете, похоже на пример ниже:

foo = new Foo();
foo = new Foo();

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

Так что я думаю, что у вас здесь происходит несколько проблем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...