-1

I started to build a game in OpenGL using GLFW and QT IDE, after i finished the drawing system, i tested it and it was extremely slow.

Here is the code: Here (Include x64 windows Libs)

there was things that i saw in the code that i could multithread, an example is the drawing function, since opengl coordinates starts with origin at the middle of the screen i had to made a drawing loop for each quadrant (-1,1| 1,-1| 1,1| -1,-1) these loops i could multithread but i think that it will not make any miracles.

GLFW + Drawing code

#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

For QT users

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

How can i change my code to make it run at a fast speed?

Ollegn
  • 2,294
  • 2
  • 16
  • 22
  • "Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it **in the question itself**." – genpfault Sep 11 '15 at 18:55
  • how can i post a specific part of the code if i dont know where the slow problem is comming from?, thats why i linked the source code. – Ollegn Sep 11 '15 at 19:48
  • You should trim it down to a [MCVE](http://stackoverflow.com/help/mcve) and edit that into the question. – genpfault Sep 11 '15 at 19:51
  • My code is already a minimal example, well, i posted it here as you asked. – Ollegn Sep 11 '15 at 19:58

1 Answers1

2

Multithreading OpenGL is almost never worth it for main rendering purposes, simply because most of the performance intensive stuff is happening on your video card: nomatter how many thread you use, you only really have 1 videocard that does the work. You can use multithreading if you want to load some external resource, a texture for instance. There's still 1 videocard, but the workload is balanced equally between threads and you don't have to see your renderloop hang for 3 seconds because you are loading a texture.

One of the reasons your program could be slow, is because you are using immediate mode. Immediate mode is an old and obsolete way of drawing. You use it by caling glBegin and glEnd with a bunch of glVertex in between. What this does is your CPU is sending all the model data to the GPU whener you draw something, even if the data doesn't change (the data basically never changes for most games). Instead what you want to do is use VBO's. Using a VBO is basically telling OpenGL: "Hey I got this enormous amount of data, I'm gonna call it Bob, so remember it". Then from that moment on you can tell opengl to "Draw bob" and it'll do so without having all the data sent back and forth 60 times a second. Switching away from immediate mode is basically where real OpenGL starts, so it has quite the learning curve. But it's all very well documented and explained.

Now there may be other reasons why your program is slow, but chances are this is a major contributor.

To see a good explanation and conversion between immediate and retained mode, see this link: What does "immediate mode" mean in OpenGL?

Henk De Boer
  • 373
  • 2
  • 6