Массовая потеря производительности с помощью glMapBuffer()?

#c #opengl #glfw #glm-math

#c #opengl #glfw #glm-математика

Вопрос:

Я обнаружил, что сопоставление буфера с доступом на запись, независимо от того, с помощью glMapBuffer, glMapBufferARB или glMapBufferRange, я всегда буду получать огромное падение FPS.

Все работало как обычно, пока я не сопоставил буфер и не разобрал его сразу после этого. Не имело значения, был ли буфер отображен во время рисования или буфер долгое время не отображался снова, я всегда получаю огромную потерю производительности. Падение FPS также происходит, когда я сопоставляю только один float (4 байта), а не весь буфер.

Я запустил код на разных графических процессорах, и вот результаты с квадрациклами 1024 * 1024:

  • NVIDIA GeForce GTX 1080 Ti:
    • Без сопоставления буфера: ~ 1100 кадров в секунду
    • При отображении буфера: ~ 200 кадров в секунду
  • NVIDIA GeForce RTX 2070:
    • Без сопоставления буфера: ~ 800 кадров в секунду
    • При отображении буфера: ~ 40 кадров в секунду
  • NVIDIA GeForce RTX 2060:
    • Без сопоставления буфера: ~ 800 кадров в секунду
    • При отображении буфера: ~ 200 кадров в секунду
  • NVIDIA GeForce MX150:
    • Без сопоставления буфера: ~ 160 кадров в секунду
    • При отображении буфера: ~ 30 кадров в секунду
  • Intel UHD Graphics 620:
    • Без сопоставления буфера: ~ 140 кадров в секунду
    • При отображении буфера: ~ 140 кадров в секунду

Как вы можете видеть на каждом графическом процессоре NVIDIA, на котором я запускал программу, я получил значительное снижение производительности с помощью glMapBuffer (на RTX 2070 частота кадров составляет всего 1/20 от обычной частоты кадров), однако на Intel UHD Graphics 620 у меня вообще не было проблем с производительностью.

Я использую:

  • OpenGL 4.6
  • glew 2.1.0
  • glfw 2.7

Теперь у меня есть несколько вопросов:

  • Почему происходит такое падение FPS?
  • Почему это только на графических процессорах NVIDIA, а не на графике Intel?
  • Не может ли NVIDIA обрабатывать буферы, которые сопоставлены или где они расположены?
  • Может ли это быть ошибкой в библиотеке?
  • Я что-то не так сделал, что-то пропустил?
  • Есть ли способ это исправить или обойти?

Вот код моей тестовой программы:

 #include <GL/glew.h>
#include <GL/glfw.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <iostream>
#include <ctime>
#include <cmath>
#include <vector>

#define WIN_WIDTH 1500
#define WIN_HEIGHT 900
#define WIN_POS_X 300
#define WIN_POS_Y 10

#define M_PI 3.14159265359

#define SPEED                   100     // moving speed
#define ROTATION_SENSETIVITY    0.002f  // mouse sensetivity

#define SQRT_QUAD_COUNT         1024    // num quads: 1024 * 1024
#define QUAD_SPACE              0.2f    // Space between quads

uint32_t atXY(uint32_t x, uint32_t y, uint32_t xSize)
{
    return y*xSize   x;
}

void gen_shaders(uint32_tamp; shader_program)
{
    std::string vertex_shader_code      =   "#version 460 coren"
                                            "layout (location = 0) in vec3 aPos;n"
                                            "uniform mat4 MVP;n"
                                            "void main()n"
                                            "{n"
                                            "   gl_Position = MVP * vec4(aPos, 1.0f);n"
                                            "}";

    std::string fragment_shader_code    =   "#version 460 coren"
                                            "out vec4 outColor;n"
                                            "void main()n"
                                            "{n"
                                            "   outColor = vec4(0.0f, 1.0f, 1.0f, 1.0f);n"
                                            "}";

    const char* hilfe1 = vertex_shader_code.c_str();
    const char* hilfe2 = fragment_shader_code.c_str();

    uint32_t vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex_shader, 1, amp;hilfe1, NULL);
    glCompileShader(vertex_shader);

    uint32_t fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment_shader, 1, amp;hilfe2, NULL);
    glCompileShader(fragment_shader);

    shader_program = glCreateProgram();
    glAttachShader(shader_program, vertex_shader);
    glAttachShader(shader_program, fragment_shader);
    glLinkProgram(shader_program);

    glDeleteShader(vertex_shader);
    glDeleteShader(fragment_shader);
}

void initWindow(int xsize, int ysize, int xpos, int ypos)
{
    if(!glfwOpenWindow(xsize, ysize, 24, 24, 24, 24, 24, 0, GLFW_WINDOW))
    {
        glfwTerminate();
        exit(-1);
    }
    glfwSetWindowPos(xpos, ypos);
}

void gen_vertices(std::vector<float>amp; vertices)
{
    for(int y=0; y<SQRT_QUAD_COUNT; y  )
    {
        for(int x=0; x<SQRT_QUAD_COUNT; x  )
        {
            const float posx = (1.0f   QUAD_SPACE) * x;
            const float posy = (1.0f   QUAD_SPACE) * y;

            const float curr_vertices[12] = {
                        posx,         posy, 10.0f,
                1.0f    posx,         posy, 10.0f,
                1.0f    posx, 1.0f    posy, 10.0f,
                        posx, 1.0f    posy, 10.0f

            };

            for(int i=0; i<12; i  )
                vertices.push_back(curr_vertices[i]);
        }
    }
}
void mouse_action(int sx, int sy, doubleamp; rotx, doubleamp; roty, float sensetivity)
{
    constexpr double roty_max = 89.9/180.0*M_PI;
    const int sx_half = sx / 2;
    const int sy_half = sy / 2;

    int mx, my;
    glfwGetMousePos(amp;mx, amp;my);

    const int deltax = sx_half - mx;
    const int deltay = sy_half - my;

    rotx  = deltax * sensetivity;
    roty  = deltay * sensetivity;

    if(rotx >= 2*M_PI)
        rotx -= 2*M_PI;
    else if(rotx <= -2*M_PI)
        rotx  = 2*M_PI;

    if(roty > roty_max)
        roty = roty_max;
    else if(roty < -roty_max)
        roty = -roty_max;

    glfwSetMousePos(sx_half, sy_half);
}

void move_action(doubleamp; posx, doubleamp; posy, doubleamp; posz, double rotx, float speed)
{
    static double old_time = glfwGetTime();
    double deltatime = glfwGetTime() - old_time;
    old_time = glfwGetTime();

    if(glfwGetKey('W') == GLFW_PRESS)
    {
        posx  = speed * deltatime * sin(rotx);
        posz  = speed * deltatime * cos(rotx);
    }
    if(glfwGetKey('A') == GLFW_PRESS)
    {
        posx  = speed * deltatime * sin(rotx   M_PI / 2);
        posz  = speed * deltatime * cos(rotx   M_PI / 2);
    }
    if(glfwGetKey('S') == GLFW_PRESS)
    {
        posx  = speed * deltatime * sin(rotx   M_PI);
        posz  = speed * deltatime * cos(rotx   M_PI);
    }
    if(glfwGetKey('D') == GLFW_PRESS)
    {
        posx  = speed * deltatime * sin(rotx   M_PI * 1.5);
        posz  = speed * deltatime * cos(rotx   M_PI * 1.5);
    }
    if(glfwGetKey(GLFW_KEY_SPACE) == GLFW_PRESS)
        posy  = speed * deltatime;
    if(glfwGetKey(GLFW_KEY_LSHIFT) == GLFW_PRESS)
        posy -= speed * deltatime;
}

int main()
{
    int width, height;
    bool running = true;

    glfwInit();
    initWindow(WIN_WIDTH, WIN_HEIGHT, WIN_POS_X, WIN_POS_Y);
    glfwSetMousePos(WIN_WIDTH/2, WIN_HEIGHT/2);
    glfwDisable(GLFW_MOUSE_CURSOR);
    glewInit();
    glfwSwapInterval(0);

    uint32_t shader_program;
    uint32_t vao, vbo;

    std::vector<float> vertices;

    double posx = 0.0;
    double posy = 0.0;
    double posz = 0.0;
    double rotx = 0.0;
    double roty = 0.0;

    gen_shaders(shader_program);    // generate the shaders
    gen_vertices(vertices);         // generate the vertex data

    glGenVertexArrays(1, amp;vao); // generate vao
    glGenBuffers(1, amp;vbo);      // generate vbo

    // bind both
    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_DYNAMIC_DRAW);   // fill buffer with vertex data
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), NULL);   // set attribute pointer ( 0 )

    // in this #if - #endif - block are the 2 lines of code that cause the problem / FPS drop:
    #if 1
    void* ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); // <----- map the buffer

    if(ptr == NULL) // does not return with -1
        return -1;

    glUnmapBuffer(GL_ARRAY_BUFFER); // <------ unmap the buffer

    if(glGetError() != 0)   // does not return with -2
        return -2;
    #endif

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    int frames = 0;
    clock_t time = clock();
    while(running)
    {
        // get fps
        if(clock() - time >= 1000)
        {
            std::cout << frames << std::endl;
            time = clock();
            frames = 0;
        }

        glfwGetWindowSize(amp;width, amp;height);
        height = height > 0 ? height : 1;

        glViewport(0, 0, width, height);
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        mouse_action(width, height, rotx, roty, ROTATION_SENSETIVITY);
        move_action(posx, posy, posz, rotx, SPEED);

        glUseProgram(shader_program);
        glm::mat4 model(1.0f);
        glm::mat4 projection              = glm::perspective(glm::radians(100.0f), (float)width/(float)height, 0.1f, 1000.0f);
        glm::mat4 view                    = glm::lookAt(glm::dvec3(posx, posy, posz), glm::dvec3(posx cos(roty)*sin(rotx), posy sin(roty), posz cos(rotx)*cos(roty)), glm::dvec3(0.0, 1.0, 0.0));

        uint32_t mvp_loc = glGetUniformLocation(shader_program, "MVP");
        glUniformMatrix4fv(mvp_loc, 1, GL_FALSE, glm::value_ptr(projection * view * model));

        glEnable(GL_DEPTH_TEST);

        glBindVertexArray(vao);
        glDrawArrays(GL_QUADS, 0, vertices.size() / 3); // draw everything
        glBindVertexArray(0);

        glDisable(GL_DEPTH_TEST);
        glUseProgram(0);

        glfwSwapBuffers();
        frames  ;

        // exit if ESC was pressed or window was closed
        running = !glfwGetKey(GLFW_KEY_ESC) amp;amp; glfwGetWindowParam( GLFW_OPENED);
    }

    glDeleteProgram(shader_program);
    glDeleteBuffers(1, amp;vbo);
    glDeleteVertexArrays(1, amp;vao);
    glfwTerminate();

    return 0;
}
  

Комментарии:

1. Вероятно, это происходит быстро на графических процессорах Intel, потому что графический процессор Intel использует те же чипы памяти, что и ваш процессор, а графический процессор nVidia — нет. Вы проверяли, отличается ли производительность в разных режимах отображения? Кроме того, вы должны измерять разницу в миллисекундах на кадр, а не в FPS.

2.Возможно, это «… Например, такие сопоставления могут быть помечены как некэшируемые области памяти, и в таких случаях чтение из них может быть очень медленным. Для обеспечения оптимальной производительности клиент должен использовать сопоставление в соответствии со значениями GL_BUFFER_USAGE для объекта buffer и access. Использование сопоставления, несовместимого с этими значениями, может быть на несколько порядков медленнее, чем использование обычной памяти «. khronos.org/registry/OpenGL-Refpages/gl4/html/glMapBuffer.xhtml

3. Ваше измерение времени полностью нарушено, clock функция должна возвращать использованное процессорное время с CLOCKS_PER_SEC тактовой частотой, а не время настенных часов в миллисекундах, хотя Microsoft использует несоответствующую реализацию, которая это делает. Кроме того, в отладочных выводах nvidia GL очень подробно описывается, где он размещает или перемещает буфер. Таким образом, можно предположить, что драйвер помещает эту память в оперативную память хоста вместо видеопамяти, что будет противоречить как вашей подсказке об использовании, так и шаблону использования, который увидит ваш драйвер.

4. Кстати, я не вижу никакой разницы в производительности на GTX 1060.

5. Проверьте, glUnmapBuffer возвращает false ли он перед безусловным вызовом glGetError .