Инстансинг с OpenGL 3.3 кажется очень медленным

Я написал минимальный пример кода на C ++, который отображает 10000 цветных
квадроциклы на экране. Я использую «instancing» и поэтому обновляю только
модель-матрица для каждого четырехугольника каждого кадра. Данные 6 вершин
хранятся в отдельном VBO и будут использоваться повторно все время.
Проекционная матрица (орфографическая) вводится один раз при запуске программы
через униформу. Модель-матрица рассчитана на CPU с библиотекой GLM.
Я измерил время рендеринга и получил средний FPS 52.
Я думаю, что это намного меньше, но я не могу найти ошибку / узкое место в моей маленькой программе-образце.

После некоторого анализа кажется, что 3 вычисления сделаны с GLM
очень медленные Я что-то здесь не так делаю? Например, если
Я снимаю вращение-расчет, я получаю FPS-Boost 10 FPS!
Может быть, вы можете помочь мне узнать, что я могу сделать лучше здесь и как
Могу ли я оптимизировать мой образец. Для меня важно, чтобы каждый квад был индивидуально настраиваемым во время выполнения, поэтому я решил использовать инстансинг.
Перенос матриц-вычислений на GPU кажется еще одним вариантом, но я действительно запутался, почему у CPU так много проблем с вычислением 10000
модель-матрицы! Хорошо, у меня очень плохой процессор (Athlon 2 Core-Duo M300, GPU — ATI Mobility Radeon 4100), но он должен выполнить эту задачу в кратчайшие сроки, или?

Вот минимальный, полностью рабочий, компилируемый пример (если у вас есть GLFW и GLM).
Может быть, у кого-то есть время и может помочь мне здесь 🙂

#define GLEW_STATIC
#define GLM_FORCE_INLINE
#define GLM_FORCE_SSE2
#include "glew.h"#include "glfw3.h"#include "glm.hpp"#include "glm/gtc/matrix_transform.hpp"#include <conio.h>
#include <cstdlib>
#include <iostream>
#include <ctime>

GLuint buildShader()
{
std::string strVSCode =
"#version 330 core\n""in vec3 vertexPosition;\n""in mat4 modelMatrix;\n""uniform mat4 projectionMatrix;\n""out vec4 m_color;\n""void main() {\n""   vec4 vecVertex = vec4(vertexPosition, 1);\n""   gl_Position = projectionMatrix * modelMatrix * vecVertex;\n""   m_color = gl_Position;\n""}\n";

std::string strFSCode = "#version 330 core\n""out vec4 frag_colour;\n""in vec4 m_color;\n""void main() {\n""   frag_colour = vec4(m_color.x, m_color.y, m_color.z, 0.5f);\n""}\n";

GLuint gluiVertexShaderId = glCreateShader(GL_VERTEX_SHADER);
char const * VertexSourcePointer = strVSCode.c_str();
glShaderSource(gluiVertexShaderId, 1, &VertexSourcePointer, NULL);
glCompileShader(gluiVertexShaderId);
GLuint gluiFragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
char const * FragmentSourcePointer = strFSCode.c_str();
glShaderSource(gluiFragmentShaderId, 1, &FragmentSourcePointer, NULL);
glCompileShader(gluiFragmentShaderId);
GLuint gluiProgramId = glCreateProgram();
glAttachShader(gluiProgramId, gluiVertexShaderId);
glAttachShader(gluiProgramId, gluiFragmentShaderId);
glLinkProgram(gluiProgramId);
glDeleteShader(gluiVertexShaderId);
glDeleteShader(gluiFragmentShaderId);
return gluiProgramId;
}

struct Sprite
{
glm::vec3 position, dimension;
float speed, rotation, rx, ry;
};

struct Vertex
{
float x, y, z;
Vertex(){};
Vertex(float x, float y, float z) : x(x), y(y), z(z) {}
};

int main(int arc, char **argv)
{
// GLFW init
int displayResWith   = 1366; //modify this here
int displayResHeight = 768;  //modify this here
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, 1);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RED_BITS, 8);
glfwWindowHint(GLFW_GREEN_BITS, 8);
glfwWindowHint(GLFW_BLUE_BITS, 8);
glfwWindowHint(GLFW_ALPHA_BITS, 8);
glfwWindowHint(GLFW_DEPTH_BITS, 32);
glfwWindowHint(GLFW_STENCIL_BITS, 32);
GLFWwindow* window = glfwCreateWindow(displayResWith, displayResHeight,"Instancing", glfwGetPrimaryMonitor(),NULL);
int width, height;
glfwMakeContextCurrent(window);
glfwSwapInterval(0);
glfwGetFramebufferSize(window, &width, &height);

//GLEW init
glewExperimental = GL_TRUE;
glewInit();
const GLubyte* renderer = glGetString(GL_RENDERER);
const GLubyte* version = glGetString(GL_VERSION);
std::cout << "Renderer: " << renderer << std::endl;
std::cout << "OpenGL supported version: " << version << std::endl;

//OpenGL init
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(255.0f, 255.0f, 255.0f, 255.0f);

//Shader
GLuint programID = buildShader();

//VBO vertexBuffer
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
Vertex VertexBufferData[6];
VertexBufferData[0] = Vertex(-0.5f, 0.5f, 0.0f);    //Links oben
VertexBufferData[1] = Vertex(-0.5f, -0.5f, 0.0f);   //Links unten
VertexBufferData[2] = Vertex(0.5f, -0.5f, 0.0f);    //Rechts unten
VertexBufferData[3] = VertexBufferData[2];          //Rechts unten
VertexBufferData[4] = Vertex(0.5f, 0.5f, 0.0f);     //Rechts oben
VertexBufferData[5] = VertexBufferData[0];          //Links oben
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*6, VertexBufferData, GL_STATIC_DRAW);

//VBO instanceBuffer
GLuint instanceBuffer;
glGenBuffers(1, &instanceBuffer);
glBindBuffer(GL_ARRAY_BUFFER, instanceBuffer);
int iMaxInstanceCount = 30000;
glm::mat4 *ptrInstanceBufferData = new glm::mat4[iMaxInstanceCount];
glBufferData(GL_ARRAY_BUFFER, iMaxInstanceCount * sizeof(glm::mat4), NULL, GL_STREAM_DRAW);

//VAO - Start
GLuint vertexArrayObject;
glGenVertexArrays(1, &vertexArrayObject);
glBindVertexArray(vertexArrayObject);

//For VBO vertexbuffer
glEnableVertexAttribArray(glGetAttribLocation(programID, "vertexPosition"));
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexAttribPointer(
glGetAttribLocation(programID, "vertexPosition"),
3,
GL_FLOAT,
GL_FALSE,
sizeof(Vertex),
(void*)0
);

glVertexAttribDivisor(0, 0);

//For VBO instanceBuffer
int pos = glGetAttribLocation(programID, "modelMatrix");
int pos1 = pos + 0;
int pos2 = pos + 1;
int pos3 = pos + 2;
int pos4 = pos + 3;
glEnableVertexAttribArray(pos1);
glEnableVertexAttribArray(pos2);
glEnableVertexAttribArray(pos3);
glEnableVertexAttribArray(pos4);
glBindBuffer(GL_ARRAY_BUFFER, instanceBuffer);
glVertexAttribPointer(pos1, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4 * 4, (void*)(0));
glVertexAttribPointer(pos2, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4 * 4, (void*)(sizeof(float) * 4));
glVertexAttribPointer(pos3, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4 * 4, (void*)(sizeof(float) * 8));
glVertexAttribPointer(pos4, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4 * 4, (void*)(sizeof(float) * 12));
glVertexAttribDivisor(pos1, 1);
glVertexAttribDivisor(pos2, 1);
glVertexAttribDivisor(pos3, 1);
glVertexAttribDivisor(pos4, 1);

glBindVertexArray(0); //VAO - End

//Matrix vars
glm::mat4 Projection, Rotating, Scaling, Translation, Identity;
glm::vec3 ZRotateVec(0.0f, 0.0f, 1.0f);

//Calc projection-matrix and put shader (uniform)
Projection = glm::ortho(0.0f, (float)width, 0.0f, (float)height, 0.0f, 1.0f);
glUseProgram(programID);
glUniformMatrix4fv(glGetUniformLocation(programID, "projectionMatrix"), 1, GL_FALSE, &Projection[0][0]);

//Creating sprites
std::srand(static_cast<unsigned int>(std::time(0)));
int iActInstanceCount = 10000;
Sprite *ptrSprites = new Sprite[iActInstanceCount];
for (int i = 0; i < iActInstanceCount; ++i)
{
ptrSprites[i].dimension = glm::vec3(16, 16, 1.0f);
ptrSprites[i].position = glm::vec3(std::rand()%(width-32),std::rand()%(height-32),-1.0f *((std::rand()%256)/256.0f));
ptrSprites[i].rotation = rand() % 360 + 0.0f;
ptrSprites[i].rx = static_cast<float>(std::rand() % 2);
ptrSprites[i].ry = static_cast<float>(std::rand() % 2);
ptrSprites[i].speed = (std::rand() % 100) + 1.0f;
if (ptrSprites[i].speed < 1.0f) ptrSprites[i].speed = 1.0f;
}

//FPS init
double fFramesRendered = 0.0f;
double fFrameMeasurementStart = 0.0f;
double fFPS = 0.0f;
double fCurrentTime = 0.0f;
glfwSetTime(0);

//Main-loop (also renderloop)
while (!glfwWindowShouldClose(window))
{
//application-logic
if (glfwGetKey(window, GLFW_KEY_ESCAPE)== GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);

const double fNewTime = glfwGetTime();
double fDeltaTime = fNewTime - fCurrentTime;
fCurrentTime = fNewTime;

for (int i = 0; i < iActInstanceCount; ++i)
{
float fSpeed = ptrSprites[i].speed * static_cast<float>(fDeltaTime);
ptrSprites[i].rotation += fSpeed;
if (ptrSprites[i].rotation >= 360.0f) ptrSprites[i].rotation = 0.0f;
if (ptrSprites[i].rx == 1)  ptrSprites[i].position.x = ptrSprites[i].position.x + fSpeed;
if (ptrSprites[i].rx == 0)  ptrSprites[i].position.x = ptrSprites[i].position.x - fSpeed;
if (ptrSprites[i].ry == 1)  ptrSprites[i].position.y = ptrSprites[i].position.y + fSpeed;
if (ptrSprites[i].ry == 0)  ptrSprites[i].position.y = ptrSprites[i].position.y - fSpeed;
if (ptrSprites[i].position.x <= 0) ptrSprites[i].rx = 1;
if (ptrSprites[i].position.x + ptrSprites[i].dimension.x >= width) ptrSprites[i].rx = 0;
if (ptrSprites[i].position.y <= 0) ptrSprites[i].ry = 1;
if (ptrSprites[i].position.y + ptrSprites[i].dimension.y >= height) ptrSprites[i].ry = 0;

//matrix-calculations (saved in local buffer)
Translation = glm::translate(Identity, ptrSprites[i].position + glm::vec3(ptrSprites[i].dimension.x / 2.0f, ptrSprites[i].dimension.y / 2.0f, 0.0f));
Scaling = glm::scale(Translation, ptrSprites[i].dimension);
ptrInstanceBufferData[i] = glm::rotate(Scaling, ptrSprites[i].rotation, ZRotateVec);
}

//render-call
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(programID);
glBindVertexArray(vertexArrayObject);
glBindBuffer(GL_ARRAY_BUFFER, instanceBuffer);
glBufferData(GL_ARRAY_BUFFER, iMaxInstanceCount * sizeof(glm::mat4), NULL, GL_STREAM_DRAW); // Buffer orphaning
glBufferSubData(GL_ARRAY_BUFFER, 0, iActInstanceCount * sizeof(glm::mat4), ptrInstanceBufferData);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, iActInstanceCount);
glBindVertexArray(0);
glfwSwapBuffers(window);
glfwPollEvents();//FPS-stuff
++fFramesRendered;

if ((fCurrentTime*1000.0f) >= (fFrameMeasurementStart*1000.0f) + 1000.0f)
{
fFPS = ((fCurrentTime*1000.0f) - (fFrameMeasurementStart*1000.0f)) / 1000.0f * fFramesRendered;
fFrameMeasurementStart = fCurrentTime;
fFramesRendered = 0;
std::cout << "FPS: " << fFPS << std::endl;
}
}

//Termination and cleanup
glDeleteBuffers(1, &vertexBuffer);
glDeleteBuffers(1, &instanceBuffer);
glDeleteVertexArrays(1, &vertexArrayObject);
glDeleteProgram(programID);
glfwDestroyWindow(window);
glfwTerminate();
return _getch();
}

2

Решение

Что ж, после тестирования на моей машине, он определенно ограничен по процессору, поэтому ничего, что вы делаете с OGL, не будет иметь большого значения. Я получаю около 300 кадров в секунду с GCC по крайней мере на -O1, но только ~ 80 с -O0. Мой процессор очень быстрый (i7 2600k, 4,7 ГГц), но мой GPU довольно медленный (GT 520). Я тоже на Ubuntu.

Несколько быстрых идей для вещей, которые могут немного ускорить его:

  • Поместите позиции вершин в массиве в вершинном шейдере и используйте gl_VertexID для доступа к ним
  • Используйте GL_TRIANGLE_STRIP вместо GL_TRIANGLES
  • Используйте радианы для углов, иначе GLM должен их преобразовать

На самом деле, ни один из них не окажет значительного влияния. Просто убедитесь, что ваш компилятор настроен правильно, и, вероятно, ничего особенного не нужно делать.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]