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

Author Topic: Continually drawing to and blurring an image, with good performance  (Read 4439 times)

0 Members and 1 Guest are viewing this topic.

SteelGiant

  • Newbie
  • *
  • Posts: 11
    • View Profile
The general idea is to achieve an effect by drawing to an image and then blurring it every frame.

At the moment I have the effect working, but I'm not getting the performance I had hoped for.

The way it works at the moment is to have a cycle of 5 objects that the image goes through:

        sf::Image drawImage;
        drawImage.create(window.getSize().x, window.getSize().y, sf::Color::Transparent);

        sf::Texture drawBufferTexture;
        drawBufferTexture.create(drawImage.getSize().x, drawImage.getSize().y);

        sf::Sprite drawBufferSprite;
        drawBufferSprite.setTexture(drawBufferTexture);

        sf::RenderTexture drawRenderTexture;
        drawRenderTexture.create(drawImage.getSize().x, drawImage.getSize().y);

        sf::Sprite drawSprite;
        drawSprite.setTexture(drawRenderTexture.getTexture());
 

The drawing is done into drawImage, and the image is then rendered to a buffered RenderTexture with a shader to do the blurring, and then the blurred image is fed back from the texture to the original image:

        drawBufferTexture.update(drawImage);

        drawRenderTexture.clear(sf::Color::Transparent);
        drawRenderTexture.draw(drawBufferSprite, &drawBlur);
        drawRenderTexture.display();

        //Cycle texture back to image:
        drawImage = drawRenderTexture.getTexture().copyToImage();
 

and finally the sprite that now contains the blurred image is rendered:

        window.draw(drawSprite);
 

The slow bit seems to be, as expected, the cycling from the RenderTexture back into the image. I need to cycle the blurred image back so that more can be drawn to it next frame, so this needs to be achieved somehow.

Is there something I'm missing here? Is there a much faster way to do this without the copyToImage() step? Is there some way to do this all within one RenderTexture using a shader?

Typically, I would only expect to be drawing into (significantly) less than 10 percent of the image each frame if that helps.

I'm currently managing to get about 70 fps on a 1080px x 1080px image out of this using a GTX 980, and I had hoped to be able to push it to higher resolutions at higher fps. Without the copyToImage step I can easily maintain 144 fps at 2160x2160.

I managed to do this at 800x800 resolution @60fps in flash on my laptop back in ~2012, so I know in principle this effect must be doable efficiently with full access to the GPU, I'm just not sure how to achieve it.
« Last Edit: March 02, 2018, 01:14:08 am by SteelGiant »

Jonny

  • Full Member
  • ***
  • Posts: 114
    • View Profile
    • Email
Re: Continually drawing to and blurring an image, with good performance
« Reply #1 on: March 02, 2018, 01:27:10 am »
Why do you need to copy the render texture back to the image? Can you not just keep the render texture and draw to it again next frame?

rough code:

sf::RenderTexture rendertexture;
sf::Sprite sprite(rendertexture.getTexture());
while(window.isOpen())
{
    rendertexture.clear();
    rendertexture.draw(<blurred image>);
    rendertexture.display();

    window.clear();
    window.draw(sprite);
    window.display();
}
 

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11033
    • View Profile
    • development blog
    • Email
Re: Continually drawing to and blurring an image, with good performance
« Reply #2 on: March 02, 2018, 03:19:53 am »
Use a shader for the blur effect.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

SteelGiant

  • Newbie
  • *
  • Posts: 11
    • View Profile
Re: Continually drawing to and blurring an image, with good performance
« Reply #3 on: March 02, 2018, 01:10:18 pm »
Why do you need to copy the render texture back to the image? Can you not just keep the render texture and draw to it again next frame?

rough code:

sf::RenderTexture rendertexture;
sf::Sprite sprite(rendertexture.getTexture());
while(window.isOpen())
{
    rendertexture.clear();
    rendertexture.draw(<blurred image>);
    rendertexture.display();

    window.clear();
    window.draw(sprite);
    window.display();
}
 

Thanks for the reply, Jonny.

I want to copy the RenderTexture back to the image so I can draw more things there each frame, alongside the (now blurry) things that were already there. The idea with this effect is that the constant blurring dissolves things that are drawn over time, and as they are dissolving things can merge together.

I'm currently drawing to the image using setPixel() to draw arbitrary pixel information. Is there a way to do something similar directly inside a RenderTexture?


eXpl0it3r, sorry if I didn't make it clear, but I am already using a shader to achieve the blur effect, and this step does indeed have good performance.

Do you mean there is some way to combine the new pixel drawing information into a shader?

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11033
    • View Profile
    • development blog
    • Email
Re: Continually drawing to and blurring an image, with good performance
« Reply #4 on: March 02, 2018, 01:43:22 pm »
Ah, I thought you applied the blurriness on the CPU via image. So what do you do with the image exactly? If you just copy it from GPU to CPU and to GPU again, you're wasting massive performance for nothing. Just keep it as texture/render texture. If you don't want things to be removed, then don't call clear on the render texture.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

SteelGiant

  • Newbie
  • *
  • Posts: 11
    • View Profile
Re: Continually drawing to and blurring an image, with good performance
« Reply #5 on: March 02, 2018, 05:54:55 pm »
Ah, I thought you applied the blurriness on the CPU via image. So what do you do with the image exactly? If you just copy it from GPU to CPU and to GPU again, you're wasting massive performance for nothing. Just keep it as texture/render texture. If you don't want things to be removed, then don't call clear on the render texture.

Yes, my apologies, for some reason I didn't see that I could do the kind of drawing I wanted directly to a RenderTexture... I see now that in the docs there is

sf::RenderTarget::draw( const Vertex *  vertices,
                        std::size_t     vertexCount,
                        PrimitiveType   type,
                        const RenderStates &    states = RenderStates::Default
                        )      
 

this is probably exactly what I need for this purpose. I'll give it a go this weekend and see if it works out.

EDIT: Yes, that does what I wanted and works nicely.
« Last Edit: March 09, 2018, 01:04:29 am by SteelGiant »