SFML community forums

Help => Graphics => Topic started by: Jonny on March 14, 2018, 12:21:33 am

Title: sf::RenderTarget::display() not present?
Post by: Jonny on March 14, 2018, 12:21:33 am
Is there a reason it's missing?

I'd like to use some polymorphism so I can switch between rendertexture and render window neatly, but currently I have to do this for the display step in clear/draw/display cycle:

       
if (dynamic_cast<sf::RenderWindow*>(m_renderTarget))
        dynamic_cast<sf::RenderWindow*>(m_renderTarget)->display();
if (dynamic_cast<sf::RenderTexture*>(m_renderTarget))
        dynamic_cast<sf::RenderTexture*>(m_renderTarget)->display();
 

Although there may be a better way, it seems logical for RenderTarget to declare the display() method?

edit: I'd also be curious why there isn't a setSize() to match getSize()?
Title: Re: sf::RenderTarget::display() not present?
Post by: Arcade on March 14, 2018, 05:04:04 pm
The RenderTarget display() question has been brought up on the forum several times. If you search around you can find the reasoning given for why that function doesn't exist. My attempt to summarize some of those answers:

Title: Re: sf::RenderTarget::display() not present?
Post by: eXpl0it3r on March 14, 2018, 06:15:58 pm
* Using RenderTarget in a polymorphic way doesn't really make sense.
Title: Re: sf::RenderTarget::display() not present?
Post by: Jonny on March 14, 2018, 07:28:27 pm
* Using RenderTarget in a polymorphic way doesn't really make sense.

Care to elaborate? Should I be taking a different approach for my use case?

sf::RenderWindow doesn't define display() directly. It gets it through inheritance of sf::Window.

I see that, but I don't see how this relates? setActive is also declared in both sf::Window and sf::Rendertarget, why can't display() be?

There isn't a need for a display() function at the RenderTarget level. A RenderTarget doesn't necessarily need a call to indicate drawing is done. For example, unbuffered rendering.

Sure, that's why I'd propose it to be pure virtual, like the other methods which aren't necessarily needed at RenderTarget level?

RenderTexture::display() internally does different things than RenderWindow::display(). It just has the same name because the usage is similar.

Can you elaborate on why that's a problem? As I understand it many of the RenderWindow/RenderTexture functions are implemented differently, even if they are inherited?
Title: Re: sf::RenderTarget::display() not present?
Post by: eXpl0it3r on March 14, 2018, 07:34:51 pm
What's your use case?
Title: Re: sf::RenderTarget::display() not present?
Post by: Jonny on March 14, 2018, 07:38:40 pm
I want to toggle between rendering to the window, or to a rendertexture, using the standard clear/draw/display cycle. For the most part this is easily done, using an sf::RenderTarget* which implements clear() and draw() amongst others. I just hit a block when I get to display(), at which point I need to mess around with casting to get it into the right type, even though both have display() implemented.
Title: Re: sf::RenderTarget::display() not present?
Post by: eXpl0it3r on March 14, 2018, 08:14:47 pm
Why does this require a generic approach skewed towards render target?

Rendering (clear, draw, display) to a window is something you do once a cycle, while rendering to a render texture can happen many times in a cycle.
Title: Re: sf::RenderTarget::display() not present?
Post by: Laurent on March 14, 2018, 08:20:25 pm
Quote
I want to toggle between rendering to the window, or to a rendertexture
Sorry for still not answering your question, but may I ask why? (this is pure curiosity)
Title: Re: sf::RenderTarget::display() not present?
Post by: Arcade on March 14, 2018, 08:27:08 pm
By the way, my response to you was simply relaying answers from previous threads, such as this one (https://en.sfml-dev.org/forums/index.php?topic=21940.msg155355#msg155355) and this (old) one (https://en.sfml-dev.org/forums/index.php?topic=10149.msg69703#msg69703). These are likely design decisions, not technical ones, so you may need to focus more on why a generic RenderTarget should have a display() function more so than the technical feasibility of the idea. I don't have much of the background knowledge on the topic, though, so I'll leave it to others in this thread to try and provide you more satisfying answers to your questions.
Title: Re: sf::RenderTarget::display() not present?
Post by: Jonny on March 14, 2018, 08:33:12 pm
I have the commonly used "Game" class, which takes care of the main loop. I'm implementing an in-game editor, which when activated, shows the game view in a preview window, so I'd like to just switch the Game class so it renders to a RenderTexture,. When the editor is disabled, it switches back to rendering directly to the window.

I'm sure there are alternative ways, and I'm not definitively saying that this is the perfect thing to do, it just seems like a logical addition with little-to-no negative effects, and I was curious what the reason would be for not having it.
Title: Re: sf::RenderTarget::display() not present?
Post by: Laurent on March 15, 2018, 08:19:48 am
I have nothing to add to what Arcade said about the existing answers in other threads.

Back to your use case. When you switch your editor on/off, you don't just switch between RenderTexture and RenderWindow, right? Once you have your game screen rendered to a RenderTexture, I suppose you do more specific stuff with it (most likely, using it to draw some sprite later)? So why can't the call to display() be part of this specific stuff that happens after drawing?

Moreover, in most architectures that I've seen, calling display() is part of the higher-level logic, ie. it happens after the call to game.draw(sf::RenderTarget&), not inside it.
Title: Re: sf::RenderTarget::display() not present?
Post by: Jonny on March 15, 2018, 07:46:52 pm
Correct, the rendertexture is drawn to an imgui window. There's no particular reason why I couldn't call display on the rendertexture from the editor code, I'd just like to keep the game loop as consistent as possible, whether it's being edited or not.

I guess Ill need to put together an example to show you more clearly. In the meantime, any response on why there's no setSize()?
Title: Re: sf::RenderTarget::display() not present?
Post by: AlexAUT on March 16, 2018, 10:24:30 pm
any response on why there's no setSize()?

Have a look at this example:

//render scene into rendertexture and apply some post processing
renderTexture.setSize({1920, 1080});
drawNormalScene(renderTexture);
applyPostProcessing(renderTexture);
window.draw(renderTexture);
//now draw minimap
renderTexture.setSize({320,320});
drawMinimap(renderTexture);
applyBlur(renderTexture);
window.draw(renderTexture);

This code looks fine, we even reuse the rendertexture which should be great, or is it?
This code would perform horribly in comparison to just having 2 rendertextures.

RenderTextures are the heaviest resources SFML does offer (except of the window and maybe sounds?) and setSize() would not express the implications of such a call. It would have to generate at least 1 new texture, 3 at worst. Also what happens to the current content? Scale down/up to the new resolution, clip it at the borders or discard it completely? Copying the content to the new texture may need a copy to userspace and back to the new texture on the GPU...



AlexAUT
Title: Re: sf::RenderTarget::display() not present?
Post by: Jonny on March 17, 2018, 10:48:04 pm
I was just curious why there's a getter but no setter, that's all. Not terribly concerned with the performance/implementation details of a derived class, just seems a strange bit of asymmetry.
Title: Re: sf::RenderTarget::display() not present?
Post by: Jonny on March 18, 2018, 11:43:14 pm
Here's some rough example code:

#include <SFML/Graphics.hpp>

int main(int argc, char const** argv)
{
    // create a window and an off screen buffer
    sf::RenderWindow window(sf::VideoMode(800, 600), "SFML window");
   
    sf::RenderTexture buffer;
    buffer.create(800,600);
    sf::Sprite bufferSprite(buffer.getTexture());
    bufferSprite.setColor(sf::Color::Red);
   
    // Start using the window directly
    sf::RenderTarget* currentTarget = &window;

    // Load a sprite to display
    sf::Texture texture;
    if (!texture.loadFromFile("cute_image.jpg")) {
        return EXIT_FAILURE;
    }
    sf::Sprite sprite(texture);
   
    auto draw = [&sprite, &currentTarget]()
    {
        currentTarget->clear();
        currentTarget->draw(sprite);
       
        // Would like to just do target->display();
        auto rt = dynamic_cast<sf::RenderTexture*>(currentTarget);
        auto rw = dynamic_cast<sf::RenderWindow*>(currentTarget);
        if (rt) rt->display();
        else if (rw) rw->display();
    };

    bool use_window = true;
   
    // Start the game loop
    while (window.isOpen())
    {
        // Process events
        sf::Event event;
        while (window.pollEvent(event))
        {
            // Close window: exit
            if (event.type == sf::Event::Closed) {
                window.close();
            }

            // Escape pressed: exit
            if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Space) {
                use_window = !use_window;
                if (use_window)
                    currentTarget = &window;
                else
                    currentTarget = &buffer;
            }
        }
       
        draw();
        if (!use_window)
        {
            window.clear();
            window.draw(bufferSprite);
            window.display();
        }
    }

    return EXIT_SUCCESS;
}
 

Furthermore, Why don't the reasons (of which there haven't really been any clear ones...) for not doing this apply to getSize()? or setActive()?
Title: Re: sf::RenderTarget::display() not present?
Post by: Laurent on March 19, 2018, 08:17:44 am
And what I said above is that the call to display() should not be part of the drawing process.

    auto draw = [&sprite, &currentTarget]()
    {
        currentTarget->clear();
        currentTarget->draw(sprite);
    }

    // ...

        draw();
        if (!use_window)
        {
            buffer.display();
            window.clear();
            window.draw(bufferSprite);
        }
        window.display();

Because display is not directly related to drawing, it's rather about what you do of your pixel buffer once drawing is done.

Quote
Furthermore, Why don't the reasons (of which there haven't really been any clear ones...) for not doing this apply to getSize()? or setActive()?
A render target is something you can draw to. So it make sense to query its size.
And because we can draw to a render target with OpenGL calls directly, we must be able to activate its internal context, thus the reason for setActive. There will probably be no other implementation of RenderTarget in the future, but if there was, it would have to have these functions in order to be a useful render target.

On the other hand, (Render)Window::display() is not a generic operation on the render target, because what it does is to copy the content of the target to the monitor. And I can hardly see an equivalent of something related to a monitor in something that is not a window.

RenderTexture::display should really be named differently. It does not display anything, and in an ideal world it would not even be needed. But sometimes there is something to do after drawing, to ensure that everything's properly done, so I chose to use this name for consistency with windows. But now I see that this was probably an error.