Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Laggy rendering.  (Read 20932 times)

0 Members and 3 Guests are viewing this topic.

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Laggy rendering.
« on: May 03, 2014, 10:45:21 pm »
Hello everyone. I finished my game engine's prototype today and I decided to test it by making a rectangle and moving it by the arrow keys. Unfortunately the rendering lags noticeably. I believe this is a result of my engine's pipeline so I may have to explain how it works:

-GameObjects are containers of Components and hold a pointer to the Engine.
-Components are containers for data. Each component holds different types of data.
-The Engine is a container of Systems.
-Systems are containers of GameObjects.

Now every time we add a Component to the GameObject, it checks the requirements for the different systems and registers to them.
Each System has an update() function where the logic is kept. The pipeline goes through all the Systems consequently and calls their update() function.

For my example I do:

void World::run()
{
        sf::RenderWindow window(sf::VideoMode(1024, 768), "Testing");

        Engine engine;
        auto PIS = engine.addSystem<PlayerInputSystem>();
        PIS->window = &window;
        engine.addSystem<MovementSystem>();
        auto RS = engine.addSystem<RenderingSystem>();
        RS->window = &window;

        GameObject player(engine);
        player.addComponent<PlayerInputComponent>();
        player.addComponent<MovementComponent>();
        player.addComponent<VelocityComponent>();
        auto GC = player.addComponent<GraphicsComponent>();
        GC->sprite.setSize(sf::Vector2f(100,10));
        GC->sprite.setPosition(sf::Vector2f(0,0));

        sf::Clock clock;
        float elapsed;
        while (window.isOpen())
        {
                elapsed += clock.restart().asSeconds();
                while (elapsed > 1/60.f)
                {
                        engine.update(1/60.f);
                        elapsed -= 1/60.f;
                }
                RS->update();
        }
}

Here are the Components:

class VelocityComponent : public Component
{
public:
        sf::Vector2f velocity;
};

class MovementComponent : public Component
{
public:
        sf::Vector2f movement;
};

class PlayerInputComponent : public Component
{
        /* No need to add anything here */
};

class GraphicsComponent : public Component
{

public:
        sf::RectangleShape sprite;
};
 

And here are the Systems:

class PlayerInputSystem : public System
{
public:
        bool registration_condition(GameObject*);
        void update(float);
        sf::RenderWindow* window;
};

bool PlayerInputSystem::registration_condition(GameObject* object)
{
        return (object->hasComponent<PlayerInputComponent>().first &&
                object->hasComponent<VelocityComponent>().first );
}

void PlayerInputSystem::update(float dt = 0)
{
        for (auto &object : registered_objects)
        {
                auto velocity = object->getComponent<VelocityComponent>();
                velocity->velocity.x = 0;
                velocity->velocity.y = 0;
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
                {
                        velocity->velocity.x = -500;
                }
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
                {
                        velocity->velocity.x = 500;
                }
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
                {
                        velocity->velocity.y = -500;
                }
                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
                {
                        velocity->velocity.y = 500;
                }
        }
}

//*****************************************************************************************************

class MovementSystem : public System
{
public:
        bool registration_condition(GameObject*);
        void update(float);
};

bool MovementSystem::registration_condition(GameObject* object)
{
        return (object->hasComponent<MovementComponent>().first &&
                object->hasComponent<VelocityComponent>().first );
}

void MovementSystem::update(float dt = 0)
{
        for (auto &object : registered_objects)
        {
                auto velocity = object->getComponent<VelocityComponent>();
                auto delta = object->getComponent<MovementComponent>();
                delta->movement.x = velocity->velocity.x * dt;
                delta->movement.y = velocity->velocity.y * dt;
        }
}

//******************************************************************************************************

class RenderingSystem : public System
{
public:
        bool registration_condition(GameObject*);
        void update(float);
        sf::RenderWindow* window;
};

bool RenderingSystem::registration_condition(GameObject* object)
{
        return (object->hasComponent<MovementComponent>().first &&
                object->hasComponent<GraphicsComponent>().first );
}

void RenderingSystem::update(float dt = 0)
{
        window->clear();
        for (auto &object : registered_objects)
        {
                auto delta = object->getComponent<MovementComponent>();
                auto graphics = object->getComponent<GraphicsComponent>();
                graphics->sprite.move(delta->movement);
                window->draw(graphics->sprite);
        }
        window->display();
}

The engine is by no means finished. It pretty much works with pointers everywhere and I would like to add Systems that don't take part in the pipeline. I hope I am doing something wrong with the game loop or else I may have to do way more work than I can handle atm.

EDIT: Wrong forum section again. Can this be moved to the help section?
« Last Edit: May 04, 2014, 10:46:28 am by Veritas »
"Lasciate ogni speranza, voi ch'entrate"

infinitebox

  • Newbie
  • *
  • Posts: 19
    • View Profile
Re: Laggy rendering.
« Reply #1 on: May 04, 2014, 03:37:38 am »
It looks a lot like EntityX. I'd be interested in how your getComponent<> and hasComponent<> methods work.

I will take a stab at it. Depending on how the systems are stored in your data structure (and therefore ordered). During a single loop cycle, they might not get updated in the order you want (e.g. input->update-position->render), so the position update might not get updated till the next loop cycle (e.g. update-postion => input => render). I'm not sure how noticeable that would be though since it's only 1/60 of a second.

A few potential improvements:
  • In the input system, poll your inputs only once and store them, before iterating through all the objects.
  • Your render is still coupled to the logic loops since you've added the render system to your engine. It gets updated along with all the other systems?
« Last Edit: May 04, 2014, 03:39:44 am by infinitebox »

zsbzsb

  • Hero Member
  • *****
  • Posts: 1409
  • Active Maintainer of CSFML/SFML.NET
    • View Profile
    • My little corner...
    • Email
Re: Laggy rendering.
« Reply #2 on: May 04, 2014, 03:57:54 am »
Motion / MotionNET - Complete video / audio playback for SFML / SFML.NET

NetEXT - An SFML.NET Extension Library based on Thor

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Re: Laggy rendering.
« Reply #3 on: May 04, 2014, 08:30:15 am »
Quote
It looks a lot like EntityX. I'd be interested in how your getComponent<> and hasComponent<> methods work.

They are kind of a hack to be honest. A lot of ugly downcasting but in my defense I am not really using inheritance in the sense of having a parent class, I just needed a generic component container. Here is how it looks atm:

class GameObject
{
public:
        GameObject(Engine&);

        template <class T>
        std::pair<bool, std::vector<Component*>::iterator> hasComponent();

        template <class T>
        T* addComponent();

        template <class T>
        void eraseComponent();

        template <class T>
        T* getComponent();

private:
        Engine& m_engine;
        std::vector<Component*> m_components;
};

GameObject::GameObject(Engine& engine) : m_engine(engine), m_components() {}

template <class T>
std::pair<bool, std::vector<Component*>::iterator> GameObject::hasComponent()
{
        auto it = std::find_if(m_components.begin(),m_components.end(),
                [](Component* comp){ return dynamic_cast<T*>(comp); });
        return (std::make_pair(it != m_components.end(),it));
}

template <class T>
T* GameObject::addComponent()
{
        assert(!hasComponent<T>().first);
        auto comp = new T();
        m_components.push_back(comp);
        m_engine.registration_check(this);
        return comp;
}

template <class T>
void GameObject::eraseComponent()
{
        auto exists = hasComponent<T>();
        assert(exists.first);
        delete *exists.second;
        m_components.erase(exists.second);
        m_engine.unregistration_check(this);
}

template <class T>
T* GameObject::getComponent()
{
        auto exists = hasComponent<T>();
        assert(exists.first);
        return static_cast<T*>(*exists.second);
}
 

As you can see the internals are still kind of messy. I also don't like the hasComponent function returning an iterator to the component vector. It's not useful outside of the other internal functions. Remember that I wrote the engine within a few hours so I still have a lot of things to fix. I am also planning to allow the systems to remember the individual components so that they won't have to iterate through the components for every object. The asserts will go away.


Quote
I will take a stab at it. Depending on how the systems are stored in your data structure (and therefore ordered). During a single loop cycle, they might not get updated in the order you want (e.g. input->update-position->render), so the position update might not get updated till the next loop cycle (e.g. update-postion => input => render). I'm not sure how noticeable that would be though since it's only 1/60 of a second.

The update order should be fine. They are stored in a vector internally.

Quote
A few potential improvements:
  • In the input system, poll your inputs only once and store them, before iterating through all the objects.
  • Your render is still coupled to the logic loops since you've added the render system to your engine. It gets updated along with all the other systems?

I am aware of the input system being really badly written. I just needed something quick and dirty to check if I got things right. As for the render system, yes it's also updated in the pipeline. Now that I think of it it shouldn't need to exist outside of it ,it doesn't really render anything new anyway.

@zsbzsb This is an engine in the more literal sense, I needed a system to decouple things a bit and this design seemed intuitive. I didn't plan to reuse things, it just makes implementing things easier in my opinion.

UPDATE: I seperated systems from the pipeline. Now you have to manually add them to it. The rendering system is now updated as expected but the problem still persists. I will upload a video.
« Last Edit: May 04, 2014, 09:47:04 am by Veritas »
"Lasciate ogni speranza, voi ch'entrate"

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Re: Laggy rendering.
« Reply #4 on: May 04, 2014, 11:32:19 am »
I changed things a bit. The sprite is now moved in the MovementSystem. For simplicity's sake I also didn't put anything in the pipeline. The game loop is now very simple in order to demonstrate the problem better.

void World::run()
{
        sf::RenderWindow window(sf::VideoMode(1024, 768), "Testing");

        Engine engine;
        auto INPUT_SYSTEM = engine.addSystem<PlayerInputSystem>();
                INPUT_SYSTEM->window = &window;
        auto MOVEMENT_SYSTEM = engine.addSystem<MovementSystem>();
        auto RENDERING_SYSTEM = engine.addSystem<RenderingSystem>();
                RENDERING_SYSTEM->window = &window;

        GameObject player(engine);
        player.addComponent<PlayerInputComponent>();
        player.addComponent<MovementComponent>();
        player.addComponent<VelocityComponent>();
        auto GRAPHICS_COMPONENT = player.addComponent<GraphicsComponent>();
                GRAPHICS_COMPONENT->sprite.setFillColor(sf::Color::Red);
                GRAPHICS_COMPONENT->sprite.setSize(sf::Vector2f(100,100));
                GRAPHICS_COMPONENT->sprite.setPosition(sf::Vector2f(0,0));

        sf::Clock clock;
        float elapsed;
        while (window.isOpen())
        {
                elapsed = clock.restart().asSeconds();
                INPUT_SYSTEM->update();
                MOVEMENT_SYSTEM->update(elapsed);
                RENDERING_SYSTEM->update();
        }
}

Below is a video (Sorry for the bad quality) :

https://www.youtube.com/watch?v=sow38ljs25I&feature=youtu.be

UPDATE:

I logged the elapsed variable in the above loop. Even without any frame caps, it seems that I get anywhere between 30-100 fps. This looks like a performance issue and it's probably a result of not caching the components in the systems. I am starting to doubt if I can keep the systems and components as is. Pointers are not really cache friendly.
« Last Edit: May 04, 2014, 01:31:47 pm by Veritas »
"Lasciate ogni speranza, voi ch'entrate"

infinitebox

  • Newbie
  • *
  • Posts: 19
    • View Profile
Re: Laggy rendering.
« Reply #5 on: May 04, 2014, 01:46:09 pm »
I'm lost :S I would suggest writing a minimal sample that can replicate the problem.

30-100fps sounds way too low for something this barebone. From the video I can definitely see that the movement of the red square is very jerky. Hopefully the residential SFML gurus can help you :P

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Re: Laggy rendering.
« Reply #6 on: May 04, 2014, 01:59:12 pm »
Yes the performance is terrible. Technically the systems should only keep the components that they are interested in. As it is now they loop through all the components in the object and that requires downcasting among other things. This is probably the bottleneck but I have no idea if it can hurt performance to this degree.
"Lasciate ogni speranza, voi ch'entrate"

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Laggy rendering.
« Reply #7 on: May 04, 2014, 02:02:06 pm »
If you're developing engines, you should definitely use a profiler. It helps you find bottlenecks in no time and optimize code specifically. This approach is much more efficient than guessing.

And use std::unique_ptr, not raw memory management. See here for reasons.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Re: Laggy rendering.
« Reply #8 on: May 04, 2014, 02:21:08 pm »
Yes I am planning to use the stl pointers, I built the prototype in a few hours and and I could do without having to handle downcasts when using them. It was mostly a test to see if such a structure would work, the code still needs a lot of changes.

By the way, I have never touched a profiler in my life. I will try to find something that works with codeblocks, there is a probably a plugin for that. Apparently this is not recommended, I will see what I can do.
« Last Edit: May 04, 2014, 02:24:11 pm by Veritas »
"Lasciate ogni speranza, voi ch'entrate"

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Laggy rendering.
« Reply #9 on: May 04, 2014, 02:28:22 pm »
At the moment I'm working with the profiler integrated into Visual Studio, but earlier I used AMD CodeAnalyst. I don't know what's the best solution in your setting, you may have to research a bit.

Otherwise, even simple sf::Clock measurements are better than guessing, but it's a lot more cumbersome and less precise than a real profiler.

Concerning std::unique_ptr: there is no reason to not do things correctly from the beginning. I have heard this argument ("I'll rewrite it better later") so often that I've included it into the myth list in my RAII article. You actually have to think and write less when you use RAII unconditionally.
« Last Edit: May 04, 2014, 02:30:47 pm by Nexus »
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Re: Laggy rendering.
« Reply #10 on: May 04, 2014, 02:37:26 pm »
Yes I understand what you mean.  I will have to remove pointers from the interface anyway. Thanks for your help, I will try to find a profiler to work with and come back to post the results after I also make the systems cache the components.
"Lasciate ogni speranza, voi ch'entrate"

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: Laggy rendering.
« Reply #11 on: May 04, 2014, 02:38:24 pm »
Some options for profilers:

gprof : http://en.m.wikipedia.org/wiki/Gprof
Callgrind : http://valgrind.org/docs/manual/cl-manual.html (see also KCachegrind: http://kcachegrind.sourceforge.net/html/Home.html )
Intel VTune : https://software.intel.com/en-us/intel-vtune-amplifier-xe
perf : https://perf.wiki.kernel.org/index.php/Main_Page

There are plenty of other options, but the above are those that I've personally used.

Edit: remember to profile your code in release/optimized builds since performance between optimized and non-optimized builds will be very different and there's no point in optimizing something in a debug build that the compiler just removes anyway in a release build.
« Last Edit: May 04, 2014, 03:14:30 pm by Jesper Juhl »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Laggy rendering.
« Reply #12 on: May 04, 2014, 03:07:52 pm »
You can try Very Sleepy. Probably the most simple and non-intrusive profiler I've seen. Works with both Visual C++ and GCC debugging information.
Laurent Gomila - SFML developer

Veritas

  • Jr. Member
  • **
  • Posts: 79
    • View Profile
    • Email
Re: Laggy rendering.
« Reply #13 on: May 04, 2014, 04:13:30 pm »
To be honest checking things with a profiler may be an overkill. The update functions are trivial for everything but the getComponent functions. Those call the hasComponent function which uses std::find_if with a dynamic cast comparison as predicate. So we have 7 find_if calls per frame, each processing 4 components. I have read that dynamic_cast is slow and it may not be viable for high performances. As I see it dynamic casting has to go as does linear searching.

I have 3 choices:

- Get rid of the getComponent function in the systems altogether. Components could be cached in the appropriate systems. This has the downside of making the implementation of every system harder since implementing the registration/unregistration functions will be required. As it is now you simply need a registration condition.

- Use an enum for comparisons. This gets rid of dynamic_cast comparisons and it's fast. This also has the downside of having to maintain an enum with enumerations similar to the system types and requiring access to the system interface.

- Use a map with typeindex as keys. I have seen something similar in some ActionScript implementations. The getComponent and hasComponent functions should be much faster to implement and maintain. The only thing I am not sure about this is whether the performance is going to be good enough. Has anyone tested the performance of typeindex comparisons?

Thanks for the help so far and sorry if this is getting out of the forum's theme. I would ask this on stackoverflow but those tend to kill all threads talking about downcasting because apparently there is a better design for all cases.
« Last Edit: May 04, 2014, 04:22:49 pm by Veritas »
"Lasciate ogni speranza, voi ch'entrate"

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: Laggy rendering.
« Reply #14 on: May 04, 2014, 04:27:14 pm »
Checking things with a profiler is (IMHO) never overkill or a bad idea.
The days where you could read a bit of code and make a reasonable guess as to how it would perform are looong gone in all but the most trivial cases.
If you spend a bit of time looking into how modern optimizers transform the code you write and then investigate how modern hyperscalar out-of-order processors with complex cache hierarchies, branch predictors and whatnot will then execute it, you'll soon come to the realization that what your CPU ends up executing is lightyears from what you wrote and that any intuition you may have about its performance will be wrong in almost all cases.
Profile always, profile often. NEVER try to optimize something without profiling data to back up the optimization.

Profile, profile, profile (did I mention to profile?) !

 

anything