#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
.