SFML / SFML

Simple and Fast Multimedia Library
https://www.sfml-dev.org/
zlib License
10.12k stars 1.7k forks source link

RenderTexture not working in OSX on 2.4.0 #1132

Closed kavika13 closed 6 years ago

kavika13 commented 8 years ago

I get a black screen when using a render texture with SFML 2.4.0 (edit: or 2.4.1). If I switch to using the window object directly and bypass the render texture, it works correctly. If I use SFML 2.3.2 with the render texture, it also works correctly.

I am using a late-2013 Macbook Pro with the Intel Iris Pro.

Here's a small-ish program that reproduces the issue on 2.4.0:

#include <SFML/Graphics.hpp>
#include <iostream>

int main(int argc, char* argv[]) {
    sf::RenderWindow window(sf::VideoMode(1024, 768), "Repro");

    sf::RenderTexture render_texture;

    const sf::Color color(150, 50, 250);

    sf::CircleShape shape(16);
    shape.setFillColor(color);

    if(!render_texture.create(window.getSize().x, window.getSize().y)) {
        std::cerr << "Failed to create render texture\n";
        return -1;
    }

    while(window.isOpen()) {
        sf::Event event;

        while(window.pollEvent(event)) {
            if(event.type == sf::Event::Closed) {
                window.close();
            }

            if(event.type == sf::Event::KeyReleased) {
                if(event.key.code == sf::Keyboard::Escape || event.key.code == sf::Keyboard::Q) {
                    window.close();
                }
            }
        }

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

        shape.setPosition(32, 32);

        render_texture.draw(shape);

        render_texture.display();

        const sf::Texture& texture = render_texture.getTexture();
        sf::Sprite sprite(texture);

        window.draw(sprite);

        window.display();
    }

    return 0;
}
kavika13 commented 8 years ago

I also have the same problem if I use the latest dev build.

kavika13 commented 8 years ago

I built from source, and I tracked down where this starts happening to this commit: 2752bbcfb048054e27cef0005f39b6df8d68deda

When running it in the OpenGL profiler, I see that the viewport somehow keeps swapping to a 0x0 size when rendering the front buffer. It doesn't do this in older versions.

I am using OSX 10.11.5 (El Capitan).

kavika13 commented 8 years ago

In the comments, I noticed some references to window.setActive. If I call this after creating my render texture, it starts rendering correctly. None of the docs that I've seen say this should be required.

binary1248 commented 8 years ago

Strange... I can't reproduce this issue on Windows or Linux. Commit 2752bbcfb048054e27cef0005f39b6df8d68deda didn't touch any platform-specific context behaviour, so it puzzles me why it breaks just on OS X.

Can you try throwing in sf::Content::getActiveContext() calls after every line in your example and see what they return?

kavika13 commented 8 years ago
kavika13 commented 8 years ago

It also stays the same as it was after creating the render texture if I stick calls inside the loop, between draw calls, etc.

binary1248 commented 8 years ago

Just out of curiosity, does this problem exist when you use the feature/no_internal_context branch?

kavika13 commented 8 years ago

I am still getting the problem with the commit from that branch.

Contexts are now showing:

kavika13 commented 8 years ago

I have created some traces on my machine of just the OpenGL calls. The traces are against the last working commit and against the commit from feature/no_internal_context. In each trace, I have trimmed everything after the first fully setup draw loop (where the calls start to become identical each iteration).

I can throw up gists if you're interested. I could also make another trace on my machine of the first problematic commit.

kavika13 commented 8 years ago

https://gist.github.com/kavika13/b0d5c183940064b46d9072097e574451

mantognini commented 8 years ago

This might be a regression of the operating system: it works fine on Yosemite 10.10.5.

mantognini commented 8 years ago

Just had my hands on a 10.11.5 and 10.11.6 and I can confirm the bug. :-/

binary1248 commented 8 years ago

So... what exactly does this mean? Or in other words, what do we do? :confused:

kavika13 commented 8 years ago

I also notice that every time a render texture is created, the context seems to get messed up:

  1. Create render window
  2. Create render texture
  3. Call window.setActive()
  4. Create render texture

Expected: Rendering happens normally

Actual: Black screen

If I add a step 5 (another call to window.setActive()) or remove step 4, then the window renders normally. I can repro the bug no matter which of the two render textures I actually use to do my rendering.

mantognini commented 8 years ago

So... what exactly does this mean? Or in other words, what do we do?

I don't know, this goes beyond my understanding of OpenGL, sadly.

binary1248 commented 8 years ago

@kavika13 is there a reason why all the CGLSetCurrentContext calls are missing? Those are the ones that are interesting and relevant...

kavika13 commented 8 years ago

I can't really tell you why the trace looks like it does. I copied and pasted it after compiling the sample against various versions of the SFML library. If OpenGL calls are missing, it is either because my trace tool isn't very good (OSX OpenGL debugger), or because SFML isn't emitting those calls.

I am in the middle of learning OpenGL for a different project, so I might be able to actually help you out in a few weeks here ;)

binary1248 commented 8 years ago

Bump. @kavika13 any news?

kavika13 commented 8 years ago

Just that I know a bit more OpenGL now ;) Nothing about context switching, but maybe I'll learn through this. I will take a look again.

kavika13 commented 8 years ago

I am still scratching my head over it. I have tried putting in a bunch of debug print statements in various places in the code, and no smoking guns are emerging. Nearly all of them are coming up with very similar results. I only saw differences when I started looking at the OSX specific context layer, when tracing calls to which context pointer had "makecurrent" called on it. I can get you those traces if it would somehow be helpful.

Maybe, as you mentioned, the fact that we're not seeing calls to CGLSetCurrentContext at all is somehow telling? Putting up the OpenGL Profiler traces for an older version of OSX that doesn't repro the bug might be useful, so we could compare them to the traces I've produced - @mantognini ?

There were some (minor?) API changes, defining the nullability of a bunch of the OpenGL parameters and return types:

https://developer.apple.com/library/content/releasenotes/General/APIDiffsMacOSX10_11/Objective-C/OpenGL.html

I don't know if that is making a difference in being able to actually call the APIs or not, especially if SFML isn't using the lower level APIs (I can't really tell - it seems to be using the Cocoa API which I think wraps those calls...?).

There's also a few versions of release notes for OSX that mention NSOpenGLContext changes here: https://developer.apple.com/library/content/releasenotes/AppKit/RN-AppKitOlderNotes/

kavika13 commented 8 years ago

I turned on context tracing in OpenGL profiler (found the button).

I noticed that if I don't call window.setActive(); before the loop that there is a context switch between these two lines:

45 0x7fd415847e00    56.94 µs glCheckFramebufferStatus(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_COMPLETE 
46 0x7fd413064800     1.04 µs glBindTexture(GL_TEXTURE_2D, 0);

If I call window.setActive(); before the loop, then there is not a context switch at that point:

45 0x7f8f7c06c400    61.28 µs glCheckFramebufferStatus(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_COMPLETE 
46 0x7f8f7c06c400     0.71 µs glBindTexture(GL_TEXTURE_2D, 0);

When I add the call window.setActive();, then the next context switch happens right after the call to glFlush();. If I don't call it, then the contexts are not switched after the first call to glFlush(); (but they are switched just after subsequent calls, like normal).

I don't know if this helps to pinpoint anything, or if it is just another symptom.

kavika13 commented 8 years ago

I also found that if I remove the call to window.setActive, but move the call to window.display(); to be at the top of my loop, things display correctly. In that case, the contexts switch rapidly once, then go back to normal:

0x7fd4a206ba00   153.82 µs glCheckFramebufferStatus(GL_FRAMEBUFFER); returns: GL_FRAMEBUFFER_COMPLETE 
0x7fd4a2067a00  2932.84 µs CGLFlushDrawable();
0x7fd4a206ba00     0.70 µs glBindTexture(GL_TEXTURE_2D, 0);
// ... rest of calls are on 0x7fd4a206ba00 until the next glFlush(); call ...
eXpl0it3r commented 7 years ago

@kavika13 Can you give this a try again with SFML 2.4.1 - maybe there's been a fix on Apple's side?

mantognini commented 7 years ago

Although NVIDIA and Intel graphic drivers were updated as part of security updates, 10.11.6 is still triggering the issue. :-/

kavika13 commented 7 years ago

I still see the bug when linking against 2.4.1, and don't see the bug against 2.3.1.

I am now on OSX 10.12.2

binary1248 commented 7 years ago

@kavika13 Can you provide the same full traces before and after that commit as you did before, except this time with context tracing enabled? It would help greatly in understanding the problem.

binary1248 commented 6 years ago

@kavika13 and @mantognini Try this patch:

diff --git a/src/SFML/Graphics/RenderTarget.cpp b/src/SFML/Graphics/RenderTarget.cpp
index 4faa7ddb..48ec7e76 100644
--- a/src/SFML/Graphics/RenderTarget.cpp
+++ b/src/SFML/Graphics/RenderTarget.cpp
@@ -373,6 +373,12 @@ void RenderTarget::resetGLStates()
     // Check here to make sure a context change does not happen after activate(true)
     bool shaderAvailable = Shader::isAvailable();

+    // Workaround for states not being properly reset on
+    // macOS unless a context switch really takes place
+    #if defined(SFML_SYSTEM_MACOS)
+        setActive(false);
+    #endif
+
     if (setActive(true))
     {
         // Make sure that extensions are initialized
mantognini commented 6 years ago

@binary1248 This seems to do the trick, thanks.

I invite others to test this as well by checking out the bugfix/osx_render_target branch.

ghost commented 6 years ago

Have same issue. Tried to test new branch, but had some troubles. More info here: https://en.sfml-dev.org/forums/index.php?topic=22786.0

Update --- As a conclusion of my investigations: this patch didn't fixed my issue. everything renders normally if there's something drawn on RenderWindow except the sprite with RenderTexture (as in my example code).

PJB3005 commented 6 years ago

Not sure if it's related, but on SFML 2.4.2, RenderTextures work for me (via SFML.NET) but I have this really strange issue where a specific one isn't clearing, unless I call CopyToImage() on its texture.