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

Author Topic: RenderTexture reuse  (Read 12224 times)

0 Members and 3 Guests are viewing this topic.

Kipernal

  • Newbie
  • *
  • Posts: 29
    • View Profile
RenderTexture reuse
« on: June 22, 2015, 04:33:41 am »
I'm trying to find a way to have one RenderTexture that's reused multiple times throughout one frame.  Basically something like this:

Code: [Select]
// Render stuff to a reusable RenderTexture
// Use the RenderTexture's texture in a sprite

// Clear the RenderTexture, render new stuff to it
// Use the RenderTexture's texture in a different sprite

// Clear the RenderTexture, render new stuff to it
// Use the RenderTexture's texture in a different sprite

// Clear the RenderTexture, render new stuff to it
// Use the RenderTexture's texture in a different sprite

// Clear the RenderTexture, render new stuff to it
// Use the RenderTexture's texture in a different sprite

// (etc.)

My previous attempt just used a ton of RenderTextures, one for each object that needed it, but since they're not lightweight objects like normal textures are (relatively speaking), that gets out of hand really quickly, and causes a crash after too many have been allocated and memory runs out.

After that I tried just allocating a pool of RenderTextures and doling them out to whoever needed them, but RenderTextures can't be resized without arduously destroying and recreating their context (it takes about a quarter of a second on my machine--completely impossible without the user noticing), having a ton of RenderTextures that I might never need uses a lot of memory, and having a fixed size creates an artificial limit that I don't really like hanging over my head.

Finally, I tried just copying the RenderTexture's texture to a new Texture and using that, but even using glCopyImageSubData the performance hit was still too much.

So none of these options are really all that great.  I did try one other thing, but the results were kind of weird:

sf::RenderTexture renderTexture;
renderTexture.create(256, 256);


// RenderWindow creation and event loop boilerplate here
{
        window.clear();
       
        renderTexture.clear(sf::Color::Red);
        renderTexture.display();
        window.draw(QuickSprite{ 0, 0, renderTexture.getTexture() });

        renderTexture.clear(sf::Color::Blue);
        renderTexture.display();
        window.draw(QuickSprite{ 256, 256, renderTexture.getTexture() });

        window.display();
}
 

All this code is supposed to do is draw a red rectangle (via a RenderTexture) on one part of the screen and a blue one on another.  I didn't really expect it to work...and it didn't.  But what caught my attention was that instead of drawing two blue rectangles like I thought (or one of each color like I hoped), it drew two red ones, and I can't imagine why.  I also noticed while trying to figure this out that if I add renderTexture.getTexture().copyToImage() before the call to window.draw it works as I wanted, drawing a red and blue rectangle.  Specifically glGetTexImage does some magic to get it working, but that's a hack and I'm like 99% sure that even if it wasn't a performance hit I couldn't actually rely on it to do this completely unrelated task.


Anyway, this is just a very long-winded way of asking if there's any way I can get the reuse of RenderTextures working, because nothing I've come up with so far has really been satisfactory.  I'd be super appreciative if anyone out there has any ideas and could share them with me.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
AW: RenderTexture reuse
« Reply #1 on: June 22, 2015, 10:23:11 am »
Question is, what do you need the render texture for each object every frame?
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Kipernal

  • Newbie
  • *
  • Posts: 29
    • View Profile
Re: RenderTexture reuse
« Reply #2 on: June 22, 2015, 05:54:51 pm »
For a certain shader effect I need a (somewhat) high resolution rendering of the object that isn't affected by the current window's view--pulling the view out too far causes artifacts.  My basic idea was to render to a RenderTexture, then have a sprite use that texture, and then move onto the next object in line.  The main problem is that there can be a lot of these objects on the screen at once, so giving them all their own RenderTexture just isn't an option.  But that being said it also means that I don't need to actually preserve the texture's image data across frames, if that helps.

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: RenderTexture reuse
« Reply #3 on: June 23, 2015, 03:19:13 am »
I don't think it's the render textures that are causing this unexpected result. I think you should make sure that your 'QuickSprite' class is doing what you think it is. You could you share 'QuickSprite' if you want opinions on it and it might help get to the root of your problem.

I inserted your exact code into a standard SFML skeleton base (the boilerplate you referred to) and implemented your QuickSprite class as simple as I could and it works fine.
(click to show/hide)
« Last Edit: June 23, 2015, 03:21:04 am by Hapax »
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Kipernal

  • Newbie
  • *
  • Posts: 29
    • View Profile
Re: RenderTexture reuse
« Reply #4 on: June 23, 2015, 03:30:16 am »
Actually my QuickSprite was even lazier (I just directly inherited from sf::Sprite, though in retrospect I really should have just made a function that returned a normal sf::Sprite--no clue why I didn't...), but fundamentally they were the exact same thing.  I still did try copying and pasting your exact code, though, but it still shows two red rectangles on my screen.  Thanks for trying, though!  :)




That being said I did some more testing and I think I figured out what was stopping me from just reusing the RenderTexture and why it was drawing two red squares instead of two blue squares.  Sort of.  I don't want to say that it's a bug in SFML per se, but it seems really suspicious because the implication is that drawing using the same RenderTexture will actually always work...unless the two draw calls that use that RenderTexture's texture are not broken up by a call that uses a different/null texture.

Basically, this code, as mentioned before, does not work (at least on my machine):

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

// Draw the first red rectangle
renderTexture.clear(red);
renderTexture.display();
window.draw(QuickSprite{ 0, 0, renderTexture.getTexture() });

// Draw the second blue rectangle
renderTexture.clear(blue);
renderTexture.display();
window.draw(QuickSprite{ 256, 256, renderTexture.getTexture() });

window.display();
 

This code, however, does:

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

// Draw the first red rectangle
renderTexture.clear(sf::Color::Red);
renderTexture.display();
window.draw(QuickSprite{ 0, 0, renderTexture.getTexture() });

// Note this call--it seems to do nothing but without it we get two red squares
window.draw(sf::RectangleShape{});

// Draw the second blue rectangle
renderTexture.clear(sf::Color::Bed);
renderTexture.display();
window.draw(QuickSprite{ 256, 256, renderTexture.getTexture() });

window.display();
 

All it takes is a request to draw something with a null texture and suddenly I can reuse the RenderTexture to draw separate things like I wanted. I narrowed down the exact function calls that are necessary and it turns out it's the RenderWindow's applyTexture(NULL) call (and probably the call to activate(true) before that as well?).  I don't know enough about OpenGL to know why this happens, if it's just a fluke, or if it's actually an oversight in SFML.  Can anyone shed some light on this?
« Last Edit: June 23, 2015, 03:32:07 am by Kipernal »

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: RenderTexture reuse
« Reply #5 on: June 23, 2015, 03:56:29 am »
Inheriting from sf::Sprite?  :o

Your two codes have more differences that just that line. The colours theirselves are represented differently. In one code, they're variables; in the other, they're explicit SFML colours (even if one of them is "Bed"). These differences could lead on to other errors such as the variables not being set properly, or being altered by QuickSprite or something.
However, that's unlikely...

May I ask how you narrowed down the function in SFML that you think is causing your error?

Also, it would likely be helpful (to others that know more about this) if you could provide information on which version of SFML you are using, which operating system, which IDE (if you're using one), and which compiler. Whether you're running in debug or release, and whether or not you're statically linking might be of use too.
It's odd that the code I posted works differently for you so it looks like it is: in SFML somewhere, some compiler difference, or possibly a linking error. You might want to make certain that all libraries are linked correctly and you're using the latest versions of DLLs (if you're using them) etc..

EDIT: Since it makes sense to make a single sprite that lasts as long as the render texture and re-use it the same way that the render texture is re-used (rather than re-creating and destroying the sprite twice per frame), I re-wrote the code:
(click to show/hide)
Does this still give you the wrong result?
« Last Edit: June 23, 2015, 04:05:25 am by Hapax »
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Kipernal

  • Newbie
  • *
  • Posts: 29
    • View Profile
Re: RenderTexture reuse
« Reply #6 on: June 23, 2015, 04:27:35 am »
Inheriting from sf::Sprite?  :o

Not something to keep around, of course, but sf::Sprite doesn't come with a constructor that takes a position and a texture and having temporaries around just cluttered the code.  Temporary insanity is why it was a class instead of a function that just returned a sf::Sprite.  :P

Your two codes have more differences that just that line. The colours theirselves are represented differently. In one code, they're variables; in the other, they're explicit SFML colours (even if one of them is "Bed"). These differences could lead on to other errors such as the variables not being set properly, or being altered by QuickSprite or something.
However, that's unlikely...

Whoops.  Those were just mistakes I made while cleaning up the code before posting it here.  I was originally doing some other color testing stuff and that's why one version has (misnamed) variables and the other just uses the sf::Color constants.  The result doesn't change though.

May I ask how you narrowed down the function in SFML that you think is causing your error?

Stepped through the debugger, skipping over certain functions to see what caused the blue rectangle to appear.  When I found what the functions were, I temporarily made all of RenderWindow's members public and called them directly (in place of the window.draw(sf::RectangleShape{}) in the above code).  With those functions in place the blue rectangle appears.

Also, it would likely be helpful (to others that know more about this) if you could provide information on which version of SFML you are using, which operating system, which IDE (if you're using one), and which compiler. Whether you're running in debug or release, and whether or not you're statically linking might be of use too.
It's odd that the code I posted works differently for you so it looks like it is: in SFML somewhere, some compiler difference, or possibly a linking error. You might want to make certain that all libraries are linked correctly and you're using the latest versions of DLLs (if you're using them) etc..

Sorry about that--since this started as a conceptual question it never crossed my mind to post that stuff.  Here's the information:

OS: Windows 8.1, 64 bit
Graphics card: AMD Radeon R7 200 Series (2048 MB memory)
SFML versions tested: Latest development snapshot
Compiler: Visual Studio 2015 RC.  Building for 32-bit.
Linkage: Static

I also tested on a super cheap laptop I have and that also gives two red squares.  Same specs as above except for the graphics card, which is probably just an integrated Intel chip.



EDIT: Since it makes sense to make a single sprite that lasts as long as the render texture and re-use it the same way that the render texture is re-used (rather than re-creating and destroying the sprite twice per frame), I re-wrote the code:

Does this still give you the wrong result?

Yep, unfortunately.   :-\

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: RenderTexture reuse
« Reply #7 on: June 23, 2015, 05:19:14 am »
Here's the information:

OS: Windows 8.1, 64 bit
Graphics card: AMD Radeon R7 200 Series (2048 MB memory)
SFML versions tested: Latest development snapshot
Compiler: Visual Studio 2015 RC.  Building for 32-bit.
Linkage: Static

I also tested on a super cheap laptop I have and that also gives two red squares.  Same specs as above except for the graphics card, which is probably just an integrated Intel chip.
Just for comparison mine is:
OS: Windows 7, 64 bit
Graphics card: AMD Radeon 5450
SFML versions tested: 2.3 release
IDE: Visual Studio 2013.  Building for 32-bit.
Linkage: Tested both dynamic and static

Do you have other compilers available? If so, could you try it on those. If not, could you download a final release version of Visual Studio (earlier version) and try it on that?
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Kipernal

  • Newbie
  • *
  • Posts: 29
    • View Profile
Re: RenderTexture reuse
« Reply #8 on: June 23, 2015, 05:36:56 am »
Alright, I just tested the exact same code you posted above (the one without the QuickSprite) using SFML 2.3 Release, Visual Studio 2013, building for 32-bit and with dynamic linkage, but unfortunately I'm still getting two red squares.  Naturally if I add window.draw(sf::RectangleShape{}) between the two draw calls I get one red and one blue, as before.

Also should probably mention just in case that my graphics drivers are up to date.  Exact version is probably meaningless but just in case it's listed as 8.01.01.1443.

Rosme

  • Full Member
  • ***
  • Posts: 169
  • Proud member of the shoe club
    • View Profile
    • Code-Concept
Re: RenderTexture reuse
« Reply #9 on: June 23, 2015, 04:24:54 pm »
By curiosity, I tested it. Same result as Kipernal. Two red squares.

OS: Windows 8.1, 64 bit
Graphics card: Intel Integrated Graphics
SFML versions tested: 2.3 Release
Compiler: Visual Studio 2012. Building for 32 bit.
Linkage: Dynamic
GitHub
Code Concept
Twitter
Rosme on IRC/Discord

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: RenderTexture reuse
« Reply #10 on: June 23, 2015, 06:16:07 pm »
Which code have you tested? The one of Hapax without QuickSprite?

I have a setting very similar to Rosme: Windows 8.1 64 bit, Intel HD Graphics 3000, VS 2013 for 32 bit, dynamic SFML 2.3.

However I can't reproduce the problem. A blue and a red square are drawn as expected.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: RenderTexture reuse
« Reply #11 on: June 23, 2015, 07:17:09 pm »
Works for me on CentOS 7.1 (Linux) with SFML 2.3 and NVidia Quadro 2000M.

Kipernal

  • Newbie
  • *
  • Posts: 29
    • View Profile
Re: RenderTexture reuse
« Reply #12 on: June 23, 2015, 07:59:03 pm »
Just tested it on my laptop at work to see if there was any difference and it's working on that (showing one blue rectangle and one red rectangle) while the exact same executable shows two red rectangles on my home machine.  Environment there was Windows 7, 64-bit, with SFML static and integrated Intel graphics.

At any rate this shows that it's not just my build environment--the exact same binary is producing two different results on two different machines.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: RenderTexture reuse
« Reply #13 on: June 23, 2015, 08:11:55 pm »
(click to show/hide)



Windows 8.1 Pro x64
AMD Radeon R9 280X
SFML 2.3 static
« Last Edit: August 04, 2015, 10:13:02 am by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: RenderTexture reuse
« Reply #14 on: June 23, 2015, 09:55:53 pm »
Since it seemed to be only the people who are statically linking who are getting the wrong result, I re-tested it statically. It still worked and, in a similar direction of thought to you, Kipernal, I'm providing my built executable for you to test. Actually, I'm providing two as a check to see if it's just the render texture's clear() that's failing (or clearing with the wrong colour). The links to the executables are directly below the source code.

First one is the exact same code above ("without QuickSprite"):
(click to show/hide)

The second one draws a circle on the render texture after each clear (to see if it's clearing it and if it is, it's clearing with the wrong colour):
(click to show/hide)
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*