Sign in to follow this  
lexington

OpenGL Opengl ECS performance problem

Recommended Posts

Hey everyone, 

 

I have been building a component entity system for my game and so far it has gone pretty well but today I have hit a problem.

 

Having just but it a system for creating and firing bullets. when i click to fire bullets it seems that i get some weird lag (most likely performance) that makes it seem as my ship is jolting forward. After looking at the VS time while debugging, it seems that after firing the bullet it takes around 30 ms to get back to the bullet system and most of this time is being take up by the init system (taking around 25 ms setting up VAO, VBO, shaders and textures). So my question (as someone who is still learning about opengl) is how would i decrease this time for setting all of this up. I will post the relevent code for the init systems and all other classes i use below. 

 

Thanks for all help provided in advance :)

 

On a node about function times, it seems as the function "com->myshade->loadshader(com->vertexshadername.c_str(), GL_VERTEX_SHADER);" with the line "GLuint Shader = glCreateShader(type);" Is taking around 10 ms to complete which im guessing is why its taking so long but i don't really know why (the fragment call finished in less then a ms).

 

Init system

void initsystem::update(float dt, entitymanager *mymanager, bool &go)
{

    std::vector<entity*> setupentites = mymanager->returnsetupent();

    for (int i = 0; i < setupentites.size(); i++)
    {
        BasicModelcomponent *com = static_cast<BasicModelcomponent*>(setupentites[i]->getcomponent(BasicModelcomponent::TypeID));
        texturecomp *textcomp = static_cast<texturecomp*>(setupentites[i]->getcomponent(texturecomp::TypeID));
        vectorimage *entityvec = static_cast<vectorimage*>(setupentites[i]->getcomponent(vectorimage::TypeID));
        skyboxcomp *mycomp = static_cast<skyboxcomp*>(setupentites[i]->getcomponent(skyboxcomp::TypeID));

        if (com != NULL && com->setupstate == false)
        {
            com->vetrices = com->getdata->retuvetices();
            com->normals = com->getdata->retunormals();
         com->uvs = com->getdata->retutexterco();
            com->myVAO = new VAO();
            com->modeldata = new meshd();
            com->UVdata = new meshd();
            com->myshade = new shader();
        
            com->myVAO->genbuffer(1);
            com->myVAO->bindVAO();

            com->modeldata->genbuffer();

            com->modeldata->addvec3buffer(com->vetrices);

            com->modeldata->addvec3buffer(com->normals);

            com->modeldata->bufferdatavec3(GL_STATIC_DRAW);

            com->modeldata->linkdata(0, 3, 0, 0);

            int test = sizeof(glm::vec3) * com->vetrices.size();

            com->modeldata->linkdata(1, 3, 0, (void*)test);

            test += sizeof(glm::vec3) * com->normals.size();

            com->modeldata->linkdata(3, 3, 0, (void*)test);

            com->UVdata->genbuffer();

            com->UVdata->addvec2buffer(com->uvs);

            com->UVdata->bufferdatavec2(GL_STATIC_DRAW);

            com->UVdata->linkdata(2, 2, 0, 0);

            if (textcomp != NULL)
            {
                textcomp->mytexture->loadtexture();
                textcomp->mytexture->gentexture(1);
                textcomp->mytexture->twodtexturesetup(true);
            }

            com->modeldata->unbindbuffer();
            com->UVdata->unbindbuffer();

            com->myVAO->unbindVAO();

            com->myshade->loadshader(com->vertexshadername.c_str(), GL_VERTEX_SHADER);

            com->myshade->loadshader(com->fragmentshadername.c_str(), GL_FRAGMENT_SHADER);

            com->myshade->linkshader();

            bool linkok = com->myshade->linkcheck();

            shipshaderlinks *SSLcom = static_cast<shipshaderlinks*>(setupentites[i]->getcomponent(shipshaderlinks::TypeID));
            lightcomp *lightlinks = static_cast<lightcomp*>(setupentites[i]->getcomponent(lightcomp::TypeID));
            if (SSLcom != NULL)
            {
                SSLcom->shaderModelMatLocation = glGetUniformLocation(com->myshade->returnprogram(), "modelMat");
                SSLcom->shaderViewMatLocation = glGetUniformLocation(com->myshade->returnprogram(), "viewMat");
                SSLcom->shaderProjMatLocation = glGetUniformLocation(com->myshade->returnprogram(), "projMat");
                if (textcomp != NULL)
                {
                    textcomp->texture = glGetUniformLocation(com->myshade->returnprogram(), "myTextureSampler");
                    textcomp->Gamma = glGetUniformLocation(com->myshade->returnprogram(), "gamma");
                }
                if (lightlinks != NULL)
                {
                    lightlinks->strenght = glGetUniformLocation(com->myshade->returnprogram(), "strength");
                    lightlinks->cameralocation = glGetUniformLocation(com->myshade->returnprogram(), "camerapos");
                    lightlinks->shine = glGetUniformLocation(com->myshade->returnprogram(), "shine");
                }

                com->setupstate = true;
            }

        }
Edited by lexington

Share this post


Link to post
Share on other sites

com->myVAO = new VAO(); com->modeldata = new meshd(); com->UVdata = new meshd(); com->myshade = new shader();

 

If I understand well, you are creating new VAO/VBO/shaders objects each time ?

 

You simply should not. Create them once for all and reuse them.

Share this post


Link to post
Share on other sites

@_silence_

So your saying that I should create a VAO, VBO and shader at the start for each model/entity and then just reuse them?

 

Yes.

 

You will even reuse the same shaders for several models. You will even might reuse the same VAO/VBO.

Bindings are generally costly. But this will not make an application having lags. However creating and deleting might. And for sure compiling shaders.

You'll have to carefully pay attention to the data transfer from the CPU to the GPU (so to the VBO). This can be a very big pitfall.

Share this post


Link to post
Share on other sites

@_Silence_

 

I see how you could use the same program for each model as it just holds the shaders you want but i thought that the the data in the VBO could only be used for one instance of the model (then you had to add the data for the next one even though it is the same vertex data), am i wrong and you can use it for multiple of the same model, if not how would i reuse the VAO/VBO for each bullet/ship?

Share this post


Link to post
Share on other sites

You're correct that you're wrong :) you can (and should) reuse the buffers (and everything else too). In rare cases where geometry is so dynamic it has to be changed each frame you can do it, but not in a way you're doing it in your code, but rather mapped buffers and not initializing every GL resource each update call. Allocating objects in such loops each frame without any sort of pooling isn't helping either.

 

First thing I would suggest is getting rid of re-updating GL resources each frame.

Edited by noizex

Share this post


Link to post
Share on other sites

@noizex

 

Ok so let me see if i understand what is being said (do have couple of questions at the end of the post :))

 

For the shader program, i can make a class that has already pre loaded all the the shader data needed, so that when it comes to creating a new bullet or fighter, i can just call of that program to be returned and use that.

 

When it comes to the VAO and VBO i would also have a class that can return the VAO to the BasicModelcomponent so that when it comes to drawing, all i have to do is glBindVertexArray then draw.

 

Is this correct or am i missing out something.

 

also in terms of my question, what do u mean about re-updating the GL resources every frame, the init system will only ever run anything if there is an object to be created.

 

Cheers 

Share this post


Link to post
Share on other sites

Well, it's hard to say for us what InitSystem does in a big picture (so far we know it allocates a lot of resources) and how often the update is called. But if you're doing this loop for every created bullet object that should be very lightweight (check flyweight pattern for example) and creating separate buffers, shaders and so on - then it's already pretty bad for performance, doesn't have to happen each frame (and for bullets that are short-lived objects it may happen quite often). 

 

Considering you have bullets and fighter objects, looking at some basic situation you should have no need to use more than 2 different shaders (like one for solid static objects and other for some billboarding or whatever technique you use to render bullets, I don't even know if this is 3D or 2D case).

 

You're correct about VAO, you can just bind VAO and draw all same objects with it, changing only model matrix. 

Edited by noizex

Share this post


Link to post
Share on other sites

[b]@[member='noizex'][/b], Arr so instead of a loop to provide the BasicModelcomponent with what it needs, i should just ask the flyweight manager to provide back what is needed (and create a new object if needed) therefore i wont really even need the init system as everything can be setup when calling the entity manager to create the bullet. Did i leave anything out?

Share this post


Link to post
Share on other sites

Sounds good. Basically you want to separate things that are resources like vertex/index buffers, textures, shaders from your components. Maybe when creating new object ask some resource cache for needed resources and provide them to components. Important thing is to not do it for every object separately but keep one loaded version of each resource and just refer to it. 

 

This can be too much for now, but another optimisation is keeping a state cache. If you render 10 meshes and all of them use the same VAO don't set this VAO 10 times, but keep a state of currentVAO/currentShader/currentRenderState and check if you really need to make GL call. This allows to grealy save on amount of GL calls, and more so if you queue your geometry so you can sort it based on shader/material/state to achieve even less state changes. 

Share this post


Link to post
Share on other sites

@[member='noizex'], @[member='_Silence_'],  Thank you every much for the help guys, ill report back when hopefully the perfomance issues are fixed :)

 

[b]@[member='noizex'][/b], that part about the current VAO states sounds like a pretty kl idea, will have to give it ago later :)

Edited by lexington

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Partner Spotlight

  • Forum Statistics

    • Total Topics
      627636
    • Total Posts
      2978331
  • Similar Content

    • By xhcao
      Before using void glBindImageTexture(    GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format), does need to make sure that texture is completeness. 
    • By cebugdev
      hi guys, 
      are there any books, link online or any other resources that discusses on how to build special effects such as magic, lightning, etc. in OpenGL? i mean, yeah most of them are using particles but im looking for resources specifically on how to manipulate the particles to look like an effect that can be use for games,. i did fire particle before, and I want to learn how to do the other 'magic' as well.
      Like are there one book or link(cant find in google) that atleast featured how to make different particle effects in OpenGL (or DirectX)? If there is no one stop shop for it, maybe ill just look for some tips on how to make a particle engine that is flexible enough to enable me to design different effects/magic 
      let me know if you guys have recommendations.
      Thank you in advance!
    • By dud3
      How do we rotate the camera around x axis 360 degrees, without having the strange effect as in my video below? 
      Mine behaves exactly the same way spherical coordinates would, I'm using euler angles.
      Tried googling, but couldn't find a proper answer, guessing I don't know what exactly to google for, googled 'rotate 360 around x axis', got no proper answers.
       
      References:
      Code: https://pastebin.com/Hcshj3FQ
      The video shows the difference between blender and my rotation:
       
    • By Defend
      I've had a Google around for this but haven't yet found some solid advice. There is a lot of "it depends", but I'm not sure on what.
      My question is what's a good rule of thumb to follow when it comes to creating/using VBOs & VAOs? As in, when should I use multiple or when should I not? My understanding so far is that if I need a new VBO, then I need a new VAO. So when it comes to rendering multiple objects I can either:
      * make lots of VAO/VBO pairs and flip through them to render different objects, or
      * make one big VBO and jump around its memory to render different objects. 
      I also understand that if I need to render objects with different vertex attributes, then a new VAO is necessary in this case.
      If that "it depends" really is quite variable, what's best for a beginner with OpenGL, assuming that better approaches can be learnt later with better understanding?
       
    • By test opty
      Hello all,
       
      On my Windows 7 x64 machine I wrote the code below on VS 2017 and ran it.
      #include <glad/glad.h>  #include <GLFW/glfw3.h> #include <std_lib_facilities_4.h> using namespace std; void framebuffer_size_callback(GLFWwindow* window , int width, int height) {     glViewport(0, 0, width, height); } //****************************** void processInput(GLFWwindow* window) {     if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)         glfwSetWindowShouldClose(window, true); } //********************************* int main() {     glfwInit();     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);     //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);     GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", nullptr, nullptr);     if (window == nullptr)     {         cout << "Failed to create GLFW window" << endl;         glfwTerminate();         return -1;     }     glfwMakeContextCurrent(window);     if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))     {         cout << "Failed to initialize GLAD" << endl;         return -1;     }     glViewport(0, 0, 600, 480);     glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);     glClearColor(0.2f, 0.3f, 0.3f, 1.0f);     glClear(GL_COLOR_BUFFER_BIT);     while (!glfwWindowShouldClose(window))     {         processInput(window);         glfwSwapBuffers(window);         glfwPollEvents();     }     glfwTerminate();     return 0; }  
      The result should be a fixed dark green-blueish color as the end of here. But the color of my window turns from black to green-blueish repeatedly in high speed! I thought it might be a problem with my Graphics card driver but I've updated it and it's: NVIDIA GeForce GTX 750 Ti.
      What is the problem and how to solve it please?
  • Popular Now