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

Author Topic: Custom view system, slow as hell  (Read 3272 times)

0 Members and 1 Guest are viewing this topic.

Ideka

  • Newbie
  • *
  • Posts: 10
    • View Profile
Custom view system, slow as hell
« on: December 31, 2012, 01:53:53 am »
I've been working on my own custom view system.

Here:
#include <iostream>
#include <vector>
#include <SFML/Graphics.hpp>

void update(sf::Window&);

void draw(sf::RenderTarget&);

class View {
    public:
        View(sf::IntRect, sf::IntRect);
        void draw(sf::RenderTarget*);
        sf::IntRect pos;
        sf::IntRect screen_pos;
};

int main() {
    sf::RenderWindow window(sf::VideoMode(200, 200), "My window", sf::Style::Close);

    sf::RenderTexture canvas;
    canvas.create(200, 200);

    std::vector<View*> views;
    views.push_back(new View(sf::IntRect(0, 0, 50, 100), sf::IntRect(50, 0, 0, 0)));
    views.push_back(new View(sf::IntRect(50, 0, 100, 100), sf::IntRect(100, 50, 0, 0)));

    sf::Sprite spr;
    sf::RenderTexture vie;

    while (window.isOpen()) {
        update(window);

        // Draw scene in canvas.
        canvas.clear(sf::Color::White);
        draw(canvas);
        canvas.display();

        window.clear(sf::Color::Black);

        // Draw each view in window.
        for (std::vector<View*>::iterator view = views.begin(); view != views.end(); ++view) {
            spr.setTexture(canvas.getTexture());
            spr.setTextureRect((*view)->pos);
            spr.setPosition(0, 0);

            vie.create((*view)->pos.width, (*view)->pos.height);
            vie.draw(spr);

            (*view)->draw(&vie);
            vie.display();

            spr.setTexture(vie.getTexture());
            spr.setTextureRect(sf::IntRect(0, 0, (*view)->pos.width, (*view)->pos.height));
            spr.setPosition((*view)->screen_pos.left, (*view)->screen_pos.top);

            window.draw(spr);
        }
        window.display();
    }
    return 0;
}

void update(sf::Window& window) {
    sf::Event event;
    while (window.pollEvent(event)) {
        switch (event.type) {
            case sf::Event::Closed:
                window.close();
                break;
            case sf::Event::KeyPressed:
                if (event.key.code == sf::Keyboard::Escape) {
                    window.close();
                }
                break;
            default:
                break;
        }
    }
}

void draw(sf::RenderTarget& window) {
    sf::CircleShape circle;
    circle.setRadius(10);

    for (unsigned int i = 0; i < window.getSize().y; i += 10) {
        for (unsigned int j = 0; j < window.getSize().x; j += 10) {
            circle.setPosition(j + 1, i + 1);
            circle.setFillColor(sf::Color(i, j, 0));
            window.draw(circle);
        }
    }
}

View::View(sf::IntRect pos, sf::IntRect screen_pos) : pos(pos), screen_pos(screen_pos) {
}

void View::draw(sf::RenderTarget* target) {
    sf::CircleShape circle;
    circle.setRadius(5);
    circle.setFillColor(sf::Color::Red);
    circle.setPosition(10, 10);
    target->draw(circle);
}
The draw function just draws a bunch of circles in a gradient and is a placeholder for what would be drawing the entire scene.
In the main loop the scene is drawn into the RenderTexture canvas. Later, parts of it are taken, as defined by the views in the views vector, and drawn on the screen. As you can see, this involves a lot of data jugglery, mainly to crop the canvas into a RenderTexture of each view. And yeah, probably because of that, it is slow as hell, and it gets worse with more views...
But the cropped RenderTextures are neccessary to pass them to the View::draw method, because I think that's just perfect for drawing HUDs.

So... if anyone with more experience than me can come up with a way to make this faster, possibly by taking advantage of SFML views, without changing the result too much... that'd be awesome.
Any help is appreciated.

G.

  • Hero Member
  • *****
  • Posts: 1593
    • View Profile
Re: Custom view system, slow as hell
« Reply #1 on: December 31, 2012, 02:20:31 am »
 ??? What's wrong with sf::View?

Ideka

  • Newbie
  • *
  • Posts: 10
    • View Profile
Re: Custom view system, slow as hell
« Reply #2 on: December 31, 2012, 04:04:14 am »
I like having a, huh, "subtexture" of the scene for each view that I can pass around to have stuff drawn on them.
Also you can't, as far as I know, assign specific coordinates to the viewport of a sf::View...

G.

  • Hero Member
  • *****
  • Posts: 1593
    • View Profile
Re: Custom view system, slow as hell
« Reply #3 on: December 31, 2012, 02:07:24 pm »
Something like sf::View::setViewport? :p
If you need a better understanding of sf::View, there's a "tutorial" on the wiki. Sorry, I don't have enough time to explain more right now, so I hope this will help you.

Ideka

  • Newbie
  • *
  • Posts: 10
    • View Profile
Re: Custom view system, slow as hell
« Reply #4 on: December 31, 2012, 04:24:20 pm »
Yeah, my bad, I knew about sf::View::setViewport, but I thought it would be difficult to provide specific coordinates to it since the viewport must be expressed as a ratio... But fooling around with it, trying it out, it looks like it's pretty precise after all.
So now my code looks like this:
#include <iostream>
#include <vector>
#include <SFML/Graphics.hpp>

void update(sf::Window&);

void draw_scene_on(sf::RenderTarget&);

class View {
    public:
        View(sf::FloatRect, sf::FloatRect);
        void set_view(sf::RenderTarget*);
        virtual void draw(sf::RenderTarget*);

    protected:
        sf::FloatRect pos;

    private:
        void set_viewport();
        sf::View view;
        sf::Vector2u target_size;
        sf::FloatRect screen_pos;
};

int main() {
    // Create a window.
    sf::RenderWindow window(sf::VideoMode(200, 200), "My window", sf::Style::Close);

    // Create a canvas to draw the scene in.
    // I made the scene and the window the same size, but that may not
    // alwas be the case.
    sf::RenderTexture canvas;
    canvas.create(200, 200);
    // Link the canvas with a sprite to be able to draw it on the window.
    sf::Sprite spr;
    spr.setTexture(canvas.getTexture());

    // Make a vector of arbitrary test Views.
    std::vector<View*> views;
    views.push_back(new View(sf::FloatRect(0, 0, 50, 100), sf::FloatRect(50, 0, 50, 100)));
    views.push_back(new View(sf::FloatRect(50, 0, 100, 100), sf::FloatRect(100, 50, 100, 100)));
    views.push_back(new View(sf::FloatRect(10, 10, 20, 20), sf::FloatRect(10, 10, 20, 20)));
    views.push_back(new View(sf::FloatRect(10, 10, 40, 20), sf::FloatRect(10, 31, 40, 20)));
    views.push_back(new View(sf::FloatRect(10, 10, 50, 20), sf::FloatRect(10, 52, 50, 20)));

    while (window.isOpen()) {
        // This just handles events; to be able to close the window.
        update(window);

        // Clear the canvas and draw the scene on it.
        canvas.clear(sf::Color::White);
        draw_scene_on(canvas);
        canvas.display();

        // Clear the window...
        window.clear(sf::Color::Black);

        // And draw the scene on each view.
        for (unsigned i = 0; i != views.size(); ++i) {
            // The view is set by calling its set_view method. This is so
            // it can adjust its viewport to the window size.
            views[i]->set_view(&window);
            window.draw(spr);
            // Each view has a draw method which is useful for
            // drawng HUDs and stuff.
            views[i]->draw(&window);
        }
        // Set the default view back.
        window.setView(window.getDefaultView());

        window.display();
    }
    return 0;
}

void update(sf::Window& window) {
    sf::Event event;
    while (window.pollEvent(event)) {
        switch (event.type) {
            case sf::Event::Closed:
                window.close();
                break;
            case sf::Event::KeyPressed:
                if (event.key.code == sf::Keyboard::Escape) {
                    window.close();
                }
                break;
            default:
                break;
        }
    }
}

void draw_scene_on(sf::RenderTarget& target) {
    sf::CircleShape circle;
    circle.setRadius(10);

    for (unsigned int i = 0; i < target.getSize().y; i += 10) {
        for (unsigned int j = 0; j < target.getSize().x; j += 10) {
            circle.setPosition(j + 1, i + 1);
            circle.setFillColor(sf::Color(i, j, 0));
            target.draw(circle);
        }
    }
}

View::View(sf::FloatRect pos, sf::FloatRect screen_pos) : pos(pos), screen_pos(screen_pos) {
    view = sf::View(pos);
    target_size = sf::Vector2u(1, 1);
    set_viewport();
}

void View::set_view(sf::RenderTarget* target) {
    sf::Vector2u ts = target->getSize();
    if (ts != target_size) {
        target_size = ts;
        set_viewport();
    }
    target->setView(view);
}

void View::set_viewport() {
    view.setViewport(sf::FloatRect(
                screen_pos.left / target_size.x,
                screen_pos.top / target_size.y,
                screen_pos.width / target_size.x,
                screen_pos.height / target_size.y));
}

void View::draw(sf::RenderTarget* target) {
    sf::CircleShape circle;
    circle.setRadius(5);
    circle.setFillColor(sf::Color::Red);
    circle.setPosition(10, 10);
    target->draw(circle);
}
It's a lot faster now, but the result isn't exactly the same... since I pass View::draw the whole window instead of just the portion of the scene the view represents.
I'd appreciate it if anyone can come up with a workaround for that...
Thanks.
« Last Edit: December 31, 2012, 10:31:28 pm by Ideka »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Custom view system, slow as hell
« Reply #5 on: December 31, 2012, 05:37:27 pm »
If you want a bit more feedback, you should probably start by explaining what you actually want to achieve and what your code does. It's good that you've already provided the code, but without an explanation we'd have to read through the whole code numerous times just to understand what you're doing... ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Ideka

  • Newbie
  • *
  • Posts: 10
    • View Profile
Re: Custom view system, slow as hell
« Reply #6 on: December 31, 2012, 10:32:35 pm »
OK, here's a little explaination.
I have a class View that warps around sf::View to make it easier to use for my purposes. View has a method draw that takes a reference to a sf::RenderTarget, and it is called after applying its sf::View and drawing the scene on it. In my old, slow method, the sf::RenderTarget recieved by View::draw had the size of the view, and only contained what was drawn on that view (so, for instance, drawing on that sf::RenderTarget in coords (0, 0) resulted on it being drawn at the top left corner of the view, not the scene). With the new method (which uses sf::View), the whole window is passed to View::draw (so drawing in (0,0) only results in drawing at the top left corner of the view if the view happens to be at the top left corner of the scene :P). It's not a big deal, View has a sf::FloatRect pos that can be used to know where to draw, but its a bit inconvenient I think.

A hacky way I thought about is making a subclass of sf::RenderTexture that lies about its size and automatically pads stuff drawn on it. I'm about to try that, don't know how it might turn out.
EDIT: OK, apparently it can't be done because sf::Drawable has no setPosition or getPosition methods. What a disappointment.

About the code, the only important part is probably the main function. I've edited the previous post with a new version with a commented main. Don't bother much reading other parts of it.
« Last Edit: January 01, 2013, 01:13:57 am by Ideka »

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: Custom view system, slow as hell
« Reply #7 on: January 01, 2013, 01:53:45 am »
Well you still didn't mention what the purpose of your View class is, other than it's fits your needs better, but from what I read, I kind of get the feeling that you're making things too complex.

Quote
In the main loop the scene is drawn into the RenderTexture canvas. Later, parts of it are taken, as defined by the views in the views vector, and drawn on the screen.
If that is your purpose of the View class, then why don't you just use sf::Sprite::setTextureRect()?
If that's not or not only the purpose, what is then? :)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Ideka

  • Newbie
  • *
  • Posts: 10
    • View Profile
Re: Custom view system, slow as hell
« Reply #8 on: January 01, 2013, 02:26:26 am »
Well you still didn't mention what the purpose of your View class is, other than it's fits your needs better, but from what I read, I kind of get the feeling that you're making things too complex.

Quote
In the main loop the scene is drawn into the RenderTexture canvas. Later, parts of it are taken, as defined by the views in the views vector, and drawn on the screen.
If that is your purpose of the View class, then why don't you just use sf::Sprite::setTextureRect()?
I do. Or did, in the old code (first post). In the new code, I use sf::Views to accomplish the same thing.

If that's not or not only the purpose, what is then? :)
The views I want in my game are supposed to be entities just like everything else, and have events like "create", "update", "draw" and "destroy". I think that'd be convenient for, for instance, having a view follow an actor (simply write view.position = actor.position in the update event), having views animate in certain ways (shaking, zooming in and out), or (and I know I'm repeating myself here) drawing HUDs on them :).