GLFW/OpenGL在蛇类游戏中的性能问题

Performance problems with GLFW/OpenGL in a snake game

本文关键字:性能 问题 游戏 OpenGL GLFW      更新时间:2023-10-16

我开始使用GLFW和QT IDE在OpenGL中构建一个游戏,在我完成绘图系统后,我测试了它,它非常慢。

下面是代码:

我在代码中看到我可以多线程,一个例子是绘图函数,因为opengl坐标从屏幕中间的原点开始,我必须为每个象限(-1,1| 1,-1| 1,1| -1,-1)创建一个绘图循环,这些循环我可以多线程,但我认为它不会创造任何奇迹。

GLFW +绘图代码

#include <GL/glew.h>
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <iostream>
#include <fstream>
#include <glm.hpp>
#include "campo.h"
#include "ponto.h"
#define GLFW_INCLUDE_GLU
#include <GLFW/glfw3.h>
using namespace std;
using namespace glm;
static void error_callback(int error, const char* description)
{
    fputs(description, stderr);
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

GLFWwindow* iniciaGL(){    //---- Funtion to start GLFW
    GLFWwindow* window;
    glfwSetErrorCallback(error_callback);
    if (!glfwInit())
        exit(EXIT_FAILURE);
    window = glfwCreateWindow(700, 700, "Snake", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }
    glfwMakeContextCurrent(window);
    glfwSwapInterval(1);
    glfwSetKeyCallback(window, key_callback);
    return window;
}
int main()
{
    GLFWwindow* window = iniciaGL();
    Campo camp;           // ---- Campo is the snake field
    Campo::campo **cmp;
    cmp = camp.GetCampo();  //-- Get the pointer to the matrix (Field) that contains the information of each square in the field
    int ts=0,tst=0;
    while (!glfwWindowShouldClose(window))  // --- Game loop
    {
        float ratio;
        int width, height;
        glfwGetFramebufferSize(window, &width, &height);
        ratio = width / (float) height;
        glViewport(0, 0, width, height);
        glClear(GL_COLOR_BUFFER_BIT);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        //glOrtho(-ratio, ratio, -1.f, 1.f, 1.f, -1.f);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
//---------- TESTING
camp.apple();  //----- Puts an "apple" in a random position in the field
               //----- this makes random red squares appear in the screen
/*
ts++;
if(ts > 39){      //---- this will should fill the screen by inserting on red squeare at time
    ts=0;
    if(tst > 39){
        tst=0;
    }else{
    tst++;
    }
}
camp.mudaBloco(ts,tst,camp.APPLE);
*/
//----------------
//---- since opengl grid has the origin in the middle of the screen, i have to draw in each quadrant (++,+-,-+,--)
//---- thats why there is 4 fors below
for(int a=0;a<(camp.Getlar())/2;a++){//line -+
            for(int b=0;b<(camp.Getalt())/2;b++){//column
                glBegin(GL_POLYGON);
                switch (cmp[a][b])
                {
                case camp.NADA:
                        glColor3f(0,0,0);
                    break;
                case camp.SNAKE:
                        glColor3f(0,0.85f,0.20f);
                    break;
                case camp.APPLE:
                        glColor3f(1.0f,0,0.1f);
                    break;
                case camp.MURO:
                        glColor3f(0.3f,0.3f,0.3f);
                    break;

                }

               glVertex2f(-1.0f+a/(camp.Getlar()/2.0f),        1.0f-b/(camp.Getalt()/2.0f) );
               glVertex2f(-1.0f+a/(camp.Getlar()/2.0f),        1.0f-(1+b)/(camp.Getalt()/2.0f) );
               glVertex2f(-1.0f+((1+a)/(camp.Getlar()/2.0f)),  1.0f-(1+b)/(camp.Getalt()/2.0f));
               glVertex2f(-1.0f+((1+a)/(camp.Getlar()/2.0f)),  1.0f-b/(camp.Getalt()/2.0f ));
               glEnd();
            }
        }

        for(int a=0;a<(camp.Getlar())/2;a++){//line ++
            for(int b=0;b<(camp.Getalt())/2;b++){//column
                glBegin(GL_POLYGON);
                switch (cmp[a+camp.Getlar()/2][b])
                {
                case camp.NADA:
                        glColor3f(0,0,0);
                    break;
                case camp.SNAKE:
                        glColor3f(0,0.85f,0.20f);
                    break;
                case camp.APPLE:
                        glColor3f(1.0f,0,0.1f);
                    break;
                case camp.MURO:
                        glColor3f(0.3f,0.3f,0.3f);
                    break;

                }
               glVertex2f(a/(camp.Getlar()/2.0f),        1.0f-b/(camp.Getalt()/2.0f) );
               glVertex2f(a/(camp.Getlar()/2.0f),        1.0f-(1+b)/(camp.Getalt()/2.0f) );
               glVertex2f(((1+a)/(camp.Getlar()/2.0f)),  1.0f-(1+b)/(camp.Getalt()/2.0f));
               glVertex2f(((1+a)/(camp.Getlar()/2.0f)),  1.0f-b/(camp.Getalt()/2.0f) );
               glEnd();
            }
        }
        for(int a=0;a<(camp.Getlar())/2;a++){//line +-
            for(int b=0;b<(camp.Getalt())/2;b++){//column
                glBegin(GL_POLYGON);
                switch (cmp[a+camp.Getlar()/2][b+camp.Getlar()/2])
                {
                case camp.NADA:
                        glColor3f(0,0,0);
                    break;
                case camp.SNAKE:
                        glColor3f(0,0.85f,0.20f);
                    break;
                case camp.APPLE:
                        glColor3f(1.0f,0,0.1f);
                    break;
                case camp.MURO:
                        glColor3f(0.3f,0.3f,0.3f);
                    break;

                }
               glVertex2f(a/(camp.Getlar()/2.0f),        -b/(camp.Getalt()/2.0f) );
               glVertex2f(a/(camp.Getlar()/2.0f),        -(1+b)/(camp.Getalt()/2.0f) );
               glVertex2f(((1+a)/(camp.Getlar()/2.0f)),  -(1+b)/(camp.Getalt()/2.0f));
               glVertex2f(((1+a)/(camp.Getlar()/2.0f)),  -b/(camp.Getalt()/2.0f ));
               glEnd();
            }
        }
        for(int a=0;a<(camp.Getlar())/2;a++){//line --
            for(int b=0;b<(camp.Getalt())/2;b++){//column


                glBegin(GL_POLYGON);
                switch (cmp[a][b+camp.Getlar()/2])
                {
                case camp.NADA:
                        glColor3f(0,0,0);
                    break;
                case camp.SNAKE:
                        glColor3f(0,0.85f,0.20f);
                    break;
                case camp.APPLE:
                        glColor3f(1.0f,0,0.1f);
                    break;
                case camp.MURO:
                        glColor3f(0.3f,0.3f,0.3f);
                    break;

                }
               glVertex2f(-1.0f+a/(camp.Getlar()/2.0f),        -b/(camp.Getalt()/2.0f) );
               glVertex2f(-1.0f+a/(camp.Getlar()/2.0f),        -(1+b)/(camp.Getalt()/2.0f) );
               glVertex2f(-1.0f+((1+a)/(camp.Getlar()/2.0f)),  -(1+b)/(camp.Getalt()/2.0f));
               glVertex2f(-1.0f+((1+a)/(camp.Getlar()/2.0f)),  -b/(camp.Getalt()/2.0f ));
               glEnd();
            }
        }



        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwDestroyWindow(window);
    glfwTerminate();
    exit(EXIT_SUCCESS);


    return 0;
}

Campo.CPP

#include "campo.h"
using namespace std;
Campo::Campo()
{
    cmp = new campo*[Campo::lar];
    for (int x = 0; x < Campo::lar; ++x) {
        cmp[x] = new campo[Campo::alt];
        for (int y = 0; y < Campo::alt; ++y) {
            cmp[x][y] = NADA;
        }
    }
    cout << "Campo iniciado" << endl;
}
void Campo::mudaBloco(int x, int y,campo val){
    cmp[x][y]=val;
}
Campo::~Campo()
{
}
Campo::campo** Campo::GetCampo(){
    return cmp;
}
void Campo::vitoria(){
    cout << "Parabéns" << endl;
    system("pause");
    exit(0);
}
int Campo::Getlar(){
    return lar;
}
int Campo::Getalt(){
    return alt;
}
void Campo::Setalt(int alt)
{
    this->alt = alt;
}
void Campo::Setlar(int lar){
    this->lar = lar;
}
void Campo::apple(){
    ponto hold;
    bool vit=true;
    for (int a = 0; a < Campo::lar; ++a) {
        for (int b = 0; b < Campo::alt; ++b) {
            if(cmp[a][b]==NADA){
                hold.x=a;
                hold.y=b;
                campo_livre.push_back(hold);
                vit = false;
            }
        }
    }
    if(vit)
        vitoria();
    srand(apples+time(NULL));
    ponto novo =  campo_livre.at(rand()%campo_livre.size());

    mudaBloco(novo.x,novo.y,APPLE);
    apples++;
    campo_livre.clear();

}

Campo.h

#ifndef CAMPO_H
#define CAMPO_H
#pragma once
#include <vector>
#include <iostream>
#include <cstdlib>
#include <ctime>
class Campo
{
public:
    enum campo{NADA,SNAKE,APPLE,MURO}; // field may only contain these types {nothing, snake, Apple, wall}
    Campo(); //--- constructor
    ~Campo();//--- destructor
    campo **GetCampo(); //--- Field matrix getter
    void mudaBloco(int x, int y, campo val); //---- function that changes the block in a coordinates
    void apple(); //--- puts an apple at a "random" position
    void Setlar(int lar); //---- set max size x of the grid
    void Setalt(int alt); //---- set max size y of the grid
    int Getlar();
    int Getalt();
    struct ponto{  //---- this structure represents a dot in the field with the respective coordinates
        int x=0;
        int y=0;
    };
private:
    int apples=0;  //----- current number of apples
    int alt=40,lar=40; //---- default max x,y size
    campo **cmp; //--- Field matrix
    void vitoria(); //--- win function
    std::vector<ponto> campo_livre; //-- this vector maps the free spaces in the field, so instead of putting a random apple in the field, we put randomly only in the free spaces
};
#endif // CAMPO_H

QT用户

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
SOURCES += main.cpp 
    campo.cpp
QMAKE_CXXFLAGS += -std=c++14 -Ofast
INCLUDEPATH += C:/glfw/include
INCLUDEPATH += C:/glew/include
INCLUDEPATH += C:/glm/glm
LIBS +=-L"C:/glfw/build_r/src"
LIBS += -lglfw3 -lopengl32 -lglu32 -lgdi32
LIBS += -L"C:/glew/lib/Release/x64"
LIBS += -lglew32
include(deployment.pri)
qtcAddDeployment()
HEADERS += 
    campo.h

我怎样才能改变我的代码,使其运行在一个快速的速度?

对于主要渲染目的来说,多线程OpenGL几乎不值得,因为大多数性能密集型的东西都发生在你的显卡上:无论你使用多少线程,你只有一个显卡来完成这项工作。如果你想加载一些外部资源,比如纹理,你可以使用多线程。仍然有一个视频卡,但工作负载在线程之间均衡,你不必看到你的renderloop挂起3秒,因为你正在加载一个纹理。

您的程序可能很慢的原因之一是您使用了立即模式。直接模式是一种古老而过时的绘图方式。你把它叫做glBeginglEnd中间有一堆glVertex。这意味着当你绘制某些内容时,你的CPU会将所有模型数据发送给GPU,即使数据没有改变(大多数游戏的数据基本上不会改变)。相反,您要做的是使用VBO。使用VBO基本上是在告诉OpenGL:"嘿,我得到了大量的数据,我要把它叫做Bob,所以记住它"。然后从那一刻起,你可以告诉opengl"绘制鲍勃",它会这样做,而不需要每秒来回发送60次所有数据。从即时模式切换出来基本上是真正的OpenGL开始的地方,所以它有相当的学习曲线。但这一切都有很好的记录和解释。

现在可能有其他原因导致您的程序变慢,但很可能这是一个主要原因。

查看即时模式和保留模式之间的很好的解释和转换,请参阅此链接:意思是在OpenGL中?