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

Author Topic: Cannot use RenderTexture multiple times in a render loop on Mac OSX  (Read 4940 times)

0 Members and 1 Guest are viewing this topic.

jmcmorris

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
After messing with this for many hours I've managed to track down what is happening. First off, this only seems to be happening on Mac OSX (I'm on 10.9). My windows build seems to be fine. I did not test on linux yet.

Symptom: When using a RenderTexture for multiple clear/draw/display cycles within a single frame it only appears to render the final clear/draw/display but uses this texture in all of the places that the sprite using the RenderTexture.getTexture() was drawn to. This is what it looks like when trees and characters share a RenderTexture and the character is rendered last. https://twitter.com/SiegeGames/status/486307979512123392

Code: I managed to boil this down into a very simple program and reproduced the issue. Even though this should draw a red, green and blue rectangle it will instead draw 3 blue ones. One interesting thing to note that I noticed is that if you do not clear the RenderTexture then you will see were the first drawn rectangle will have the red, green and blue rectangle all overlapping. The second rectangle will only be green and blue and then the final rectangle is just blue.

void drawShape(sf::RenderWindow& window, sf::RenderTexture& texture, sf::Vector2f size, sf::Color color, sf::Vector2f position) {
    texture.clear(sf::Color(0, 0, 0, 0));
    sf::RectangleShape shape(size);
    shape.setFillColor(color);
    texture.draw(shape);
    texture.display();
    sf::Sprite sprite(texture.getTexture());
    sprite.setPosition(position);
    sprite.setTextureRect(sf::IntRect(0, 0, size.x, size.y));
    window.draw(sprite);
}

int main(int argc, char** argv) {
    // create the window
    sf::RenderWindow window(sf::VideoMode(800, 600), "My window");
    sf::RenderTexture texture;
    texture.create(32, 32);

    // run the program as long as the window is open
    while (window.isOpen())
    {
        // check all the window's events that were triggered since the last iteration of the loop
        sf::Event event;
        while (window.pollEvent(event))
        {
            // "close requested" event: we close the window
            if (event.type == sf::Event::Closed)
                window.close();
        }

        // clear the window with black color
        window.clear(sf::Color::Black);

        // draw everything here...
        drawShape(window, texture, sf::Vector2f(16, 16), sf::Color(255, 0, 0), sf::Vector2f(32, 32));
        drawShape(window, texture, sf::Vector2f(12, 24), sf::Color(0, 255, 0), sf::Vector2f(32, 64));
        drawShape(window, texture, sf::Vector2f(24, 12), sf::Color(0, 0, 255), sf::Vector2f(32, 96));

        // end the current frame
        window.display();
    }

    return 0;
}

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: Cannot use RenderTexture multiple times in a render loop on Mac OSX
« Reply #1 on: July 09, 2014, 02:56:41 pm »
It would help a lot to know if SFML is using the "fake" RenderTexture implementation using a secondary context or if it is really using an FBO. If you can step through a debug build of SFML to check which is the case, that would provide useful information. If you can't/don't want to, you can try to get the OpenGL extension strings with glGetStringi( GL_EXTENSIONS​, extension_str ) and glGetIntegerv( GL_NUM_EXTENSIONS, &num_extensions ) and paste them here so we can check if GL_ARB_framebuffer_object is provided by OS X. If you have access to some OpenGL debugging tools, you might be able to check with them instead.

Either way, this is probably related to context management or how the OpenGL driver works on OS X. The OS X OpenGL implementation claims to be the only one that "supports" real multithreading, but if that is the reason why artefacts like this show up... I don't know what they were thinking.
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

jmcmorris

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Cannot use RenderTexture multiple times in a render loop on Mac OSX
« Reply #2 on: July 09, 2014, 05:49:56 pm »
I will admit I'm a complete noob when it comes to debugging on mac since I do all debugging that I can on windows. I went for what seemed liked the easier option and tried your second suggestion. Here is the code I used.

GLint n, i;
glGetIntegerv(GL_NUM_EXTENSIONS, &n);
LOG_INFO("GL_NUM_EXTENSIONS: " << Uint32(n));
for (i = 0; i < n; i++) {
    LOG_INFO("GL_EXTENSION: " << glGetStringi(GL_EXTENSIONS, i));
}

This only logged "GL_NUM_EXTENSIONS: 0" which doesn't make any sense and I'm obviously doing this wrong. I put this after the first window.display() so I'd imagine all gl context stuff has been initialized by then (if that is even necessary for this).

I did find this https://developer.apple.com/graphicsimaging/opengl/capabilities/ and I'm on a GeForce 650. Sorry that this post is not that much help.

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: Cannot use RenderTexture multiple times in a render loop on Mac OSX
« Reply #3 on: July 09, 2014, 08:27:11 pm »
According to that matrix, you should be using an FBO.

Before trying anything else, you should give the current master a try. There was a fix not long ago that fixed an issue that sounds very similar to the one you are having.
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

jmcmorris

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Cannot use RenderTexture multiple times in a render loop on Mac OSX
« Reply #4 on: July 09, 2014, 09:06:34 pm »
According to that matrix, you should be using an FBO.

Before trying anything else, you should give the current master a try. There was a fix not long ago that fixed an issue that sounds very similar to the one you are having.
Yeah, I found that fix and the related thread with the fix you posted. Sadly, I tried the latest master (as of 2 days ago) and still was able to reproduce the issue.

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: Cannot use RenderTexture multiple times in a render loop on Mac OSX
« Reply #5 on: July 10, 2014, 02:31:48 am »
All I can say is that if SFML really uses the FBO implementation in your case, the display() should do nothing else but flush all queued commands and make sure that any that follow them source from the updated texture.

I have a slight suspicion that this really has to do with the "multi-threaded" nature of GL in OS X. Because I couldn't find any specification of whether glFlush should only apply to the calling context or all contexts within a share group, I have to assume that it is up to the implementation to decide. When running with multiple simultaneous threads, they might all have their own command queue and need not synchronize among each other (how this would work I have no idea), as such glFlush doesn't synchronize with other threads. Because glFlush only flushes the command queue in context memory (of which there can be multiple in a multi-threaded implementation) there might not be a guarantee that operations performed in different contexts within a single-threaded user application might be sent to the GPU in the same order. Bearing this in mind, your texture might only be updated when the swap command from the main context forces the GPU to resolve the texture dependency, leading to everything being queued on the RenderTexture executed at the same time.

The RenderTexture implementation assumes an ordering guarantee exists, and that is trivially the case with single-threaded drivers as is present on Windows and Linux.

You can try to grab the latest master and replace this line with glCheck(glFinish()); and see if it fixes the problem.

glFinish implicitly guarantees ordering, since unlike glFlush, it involves a CPU-GPU-CPU synchronization and from what I understand, the GPU only has a single command stream originating from the CPU, unless something has changed in recent architectures. It will most likely kill performance, but if that really fixes the problem, then the root lies within the OS X GL implementation and there is nothing we can really do until we rewrite SFML to make more use of modern OpenGL where we have more powerful synchronization facilities at our disposal.
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

jmcmorris

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Cannot use RenderTexture multiple times in a render loop on Mac OSX
« Reply #6 on: July 10, 2014, 03:15:17 am »
Haha, nothing seems to want to work. I just tried using that glFinish() instead of glFlush() with master and it still exhibits this issue. I then added logging in RenderTexture.cpp and it is indeed using FBO. Got any other ideas? :)

By the way, thank you very much for your assistance thus far. Much appreciated!
« Last Edit: July 10, 2014, 03:16:53 am by jmcmorris »

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: Cannot use RenderTexture multiple times in a render loop on Mac OSX
« Reply #7 on: July 10, 2014, 03:21:30 am »
Well... without a proper GL debugging environment, such divergent behaviour is hard to diagnose. I don't have an OS X machine at my disposal, and even if I did, maybe this problem wouldn't even reproducible on it. It would be nice if anybody else with OS X access could test this. If it really is reproducible on all OS X machines, then as I mentioned earlier, it could have something to do with the GL implementation, and we can start writing more specific tests for you to try out.
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

jmcmorris

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Cannot use RenderTexture multiple times in a render loop on Mac OSX
« Reply #8 on: July 10, 2014, 04:38:37 am »
Well... without a proper GL debugging environment, such divergent behaviour is hard to diagnose. I don't have an OS X machine at my disposal, and even if I did, maybe this problem wouldn't even reproducible on it. It would be nice if anybody else with OS X access could test this. If it really is reproducible on all OS X machines, then as I mentioned earlier, it could have something to do with the GL implementation, and we can start writing more specific tests for you to try out.
The other dev on the team is on a mac and is running into this as well. This is sounding like it'll be very difficult and somewhat unlikely that it'll be fixed, which I understand. Do you know of any good workarounds for this?

What I am trying to do is make it possible for my entities that are broken into pieces physically (such as my character with gear) be rendered to a RenderTexture so that I can apply transforms to the entire sprite and not the individual sprites. I ran into this issue when I tried sharing a RenderTexture between all of these entities. Unfortunately there are too many entities to make RenderTextures for all (I tried and it crashes). So yeah, I'm looking for work arounds. Thanks again!

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: Cannot use RenderTexture multiple times in a render loop on Mac OSX
« Reply #9 on: July 10, 2014, 05:48:11 am »
Is it infeasible to just render each piece for itself with its own transform? Unless there are a few hundred entities each with more than a handful of pieces, it shouldn't be any slower than rendering it all to a RenderTexture just to batch the transform application. RenderTextures aren't really meant for overcoming the need to apply separate transforms to separate objects, especially since the location of the pieces relative to the entity basically never changes. They are meant more for applications where feeding an object into the rendering pipeline would be faster than doing software compositing. In either case, if you only need to perform the composite once, then do it only when something changes and cache the generated image so you don't have to do it every frame, since the generated image would be the same.

If you store a transform per entity, i.e. its position relative to the scene in the entity itself along with "piece transforms" that are additionally applied to each piece after the entity transform, then you can simply chain both to get the absolute position of each piece. Since this is done in software and the transformed vertices are passed on to OpenGL, this shouldn't be that taxing on the graphics hardware. Rendering to a secondary framebuffer is way more taxing than simply multiplying and adding a few numbers with each other every frame, mainly because it has to assume that you want to make use of all the other things that are possible during rendering. Compared to sending the vertices down the rendering pipeline, the multiplications and additions are nothing ;).

If you want to get real fancy, you can always create VertexArrays out of your entity pieces to represent your entities and reference your piece textures out of a texture atlas so that you can cut down on the texture re-binding as well. Texture re-binding is also much more expensive than multiplications and additions ;).
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

jmcmorris

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Cannot use RenderTexture multiple times in a render loop on Mac OSX
« Reply #10 on: July 10, 2014, 07:37:53 am »
Thanks for the response. This is true but I also am using it to apply a shader to the final sprite and am already applying a shader to the individual parts.

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: Cannot use RenderTexture multiple times in a render loop on Mac OSX
« Reply #11 on: July 14, 2014, 04:37:40 pm »
Hmm... this still intrigues me...

If you have time to spare, can you try commenting out lines 67 and 87 and replacing line 135 with return true; in RenderTextureImplFBO.cpp and try running the code you posted again? That will disable the FBO context switches that take place and the code you posted should operate all within a single context. If it still doesn't work, we can rule out multi-threaded GL as the cause of the issue. This is important to know for future design decisions that we might make :D.
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

jmcmorris

  • Jr. Member
  • **
  • Posts: 64
    • View Profile
Re: Cannot use RenderTexture multiple times in a render loop on Mac OSX
« Reply #12 on: July 14, 2014, 04:40:15 pm »
Sure, I'll try this out soon. Probably will be a day or two.