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

Author Topic: Thread-local sf::Context  (Read 5768 times)

0 Members and 1 Guest are viewing this topic.

pdinklag

  • Sr. Member
  • ****
  • Posts: 330
  • JSFML Developer
    • View Profile
    • JSFML Website
Thread-local sf::Context
« on: March 15, 2013, 07:18:27 am »
I am currently trying to optimize the Context class in JSFML.

Right now, to acquire a valid context, the approach is exactly like in SFML: you create a new instance of the class to ensure there is an active context on the current thread and can also use it to explicitly activate or deactivate it. However, that is relatively slow, because creating the "C++" (native level) SFML object causes quite some overhead, which cannot be avoided other than minimizing the amount of communication done (much like in networking).

My idea was to cache any Context instance created thread-locally, so that any subsequent creation on the same thread would simply return the current thread-local context. This turns out to be blazingly fast, however, I believe this means that any context stays active unless somebody explicitly deactivates it using "setActive", because the object will not be garbage collected and destroyed on a native level (at least not on long-running threads like the main thread).

Is that correct and, more importantly, is that a problem?
JSFML - The Java binding to SFML.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Thread-local sf::Context
« Reply #1 on: March 15, 2013, 08:02:12 am »
A few questions to have a clearer overview of the problem:

- what is "slow"? this is typically the kind of things that you do once when the thread is launched, so does it really matter?

- why would one create more than one sf::Context in the same thread??

- sf::Context is only needed in a few very specific cases; if you use the graphics module it should not be required at all. are you sure that you're using it the right way?
Laurent Gomila - SFML developer

pdinklag

  • Sr. Member
  • ****
  • Posts: 330
  • JSFML Developer
    • View Profile
    • JSFML Website
Re: Thread-local sf::Context
« Reply #2 on: March 15, 2013, 01:37:14 pm »
I'm not entirely sure how sf::Context is used, obviously. You are right about the first two points, it won't matter if it is "slow" when it's not used often (the term "slow" I used very loosely here, it's just really slow compared to an opeation on a pure Java level, simply because of the overhead generated when calling native methods).

However, the graphics module does not seem to take care of creating a context automatically for me.
One piece of evidence was provided in http://en.sfml-dev.org/forums/index.php?topic=10452.0, where textures could not be loaded in a separate thread unless a Context instance was created, and just recently I realized that I cannot load textures before creating a window or a Context (I would end up with white squares when used as a sprite).

Is there anything I might have done (accidentally) to prevent that from working?
« Last Edit: March 15, 2013, 01:39:03 pm by pdinklag »
JSFML - The Java binding to SFML.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Thread-local sf::Context
« Reply #3 on: March 15, 2013, 01:49:55 pm »
I don't know why it doesn't work. You should test it on the same machine but directly in C++/SFML. So that we can see if it's an environment issue or a binding issue.

It might also be a Linux issue, so you should test on other OSes (in case you didn't yet).
Laurent Gomila - SFML developer

pdinklag

  • Sr. Member
  • ****
  • Posts: 330
  • JSFML Developer
    • View Profile
    • JSFML Website
Re: Thread-local sf::Context
« Reply #4 on: March 15, 2013, 02:27:33 pm »
I'm running on Windows 7.
I tried both 32 and 64 bit now, and in a simple C++ program, it works as you described, textures are fine even if loaded before a window or context is created.
        sf::Texture *texture = new sf::Texture();
        texture->loadFromFile("test.png");

        sf::RenderWindow *window =
                new sf::RenderWindow(sf::VideoMode(640, 480), "Context test");

        sf::Sprite *sprite = new sf::Sprite(*texture);

        while(window->isOpen()) {
                window->clear();
                window->draw(*sprite);
                window->display();

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

        delete sprite;
        delete window;
        delete texture;

(Just in case you wonder why I use pointers - I wanted to resemble as good as possible what happens when using the Java binding.)

The same program using the Java binding results in a white box being painted. The dimensions of the texture are correct (same as the source image), but the content is plain white. As soon as I create a sf::Context instance before creating (loading) the texture, it works just like in the program above.

So this is definitely a problem with my binding, but it is still awkward. The native code executed should be practically the same that I posted above.
I wouldn't know of any thread shenanigans going on in the background in Java, but even if so, according to you, that should not be a problem.
JSFML - The Java binding to SFML.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Thread-local sf::Context
« Reply #5 on: March 15, 2013, 02:33:59 pm »
Yes, that's really strange. I have no idea what could cause this :-\

You should now try a similar code, that loads a texture from a thread rather than before creating the window.
Laurent Gomila - SFML developer

pdinklag

  • Sr. Member
  • ****
  • Posts: 330
  • JSFML Developer
    • View Profile
    • JSFML Website
Re: Thread-local sf::Context
« Reply #6 on: March 15, 2013, 02:47:38 pm »
I will give that a try.

What I just tried (in Java) is converting the texture to an image (copyToImage) and saving it to a file - and the resulting image is OK! Yet I see a white box when using the texture in a sprite.
JSFML - The Java binding to SFML.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Thread-local sf::Context
« Reply #7 on: March 15, 2013, 03:12:54 pm »
Are you really sure that there's no hidden thread spawned internally?

What you can try now, is to call glFlush() (include and link OpenGL) right after calling texture.loadFromFile in your binding.
Laurent Gomila - SFML developer

pdinklag

  • Sr. Member
  • ****
  • Posts: 330
  • JSFML Developer
    • View Profile
    • JSFML Website
Re: Thread-local sf::Context
« Reply #8 on: March 15, 2013, 03:48:02 pm »
Are you really sure that there's no hidden thread spawned internally?
Everything is done in the main thread. Calling native code does not change threads.

What you can try now, is to call glFlush() (include and link OpenGL) right after calling texture.loadFromFile in your binding.

Tried that now, no change. :(
JNIEXPORT jboolean JNICALL Java_org_jsfml_graphics_Texture_nativeLoadFromMemory
    (JNIEnv *env, jobject obj, jbyteArray arr, jobject area) {

    std::size_t n = (std::size_t)env->GetArrayLength(arr);
    jbyte* mem = env->GetByteArrayElements(arr, 0);

    jboolean result = THIS(sf::Texture)->loadFromMemory(mem, n, JSFML::Intercom::decodeIntRect(env, area));
    glFlush();

    env->ReleaseByteArrayElements(arr, mem, JNI_ABORT);
    return result;
}

I should note that I use loadFromMemory because I handle files on a Java-level, that allows a more detailled exception handling and only needs one "load" implementation that I can use for Java input streams as well. I don't think that should make any difference.
JSFML - The Java binding to SFML.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Thread-local sf::Context
« Reply #9 on: March 15, 2013, 03:56:27 pm »
Quote
Tried that now, no change.
Ok, so it's probably not a thread issue :-\

Quote
I should note that I use loadFromMemory because I handle files on a Java-level, that allows a more detailled exception handling and only needs one "load" implementation that I can use for Java input streams as well. I don't think that should make any difference.
It doesn't make any difference. Except that your loadFromFile functions will be slower and consume much more memory than the native ones ;)
Laurent Gomila - SFML developer

pdinklag

  • Sr. Member
  • ****
  • Posts: 330
  • JSFML Developer
    • View Profile
    • JSFML Website
Re: Thread-local sf::Context
« Reply #10 on: March 16, 2013, 07:54:58 am »
True that, but I haven't touched the loading routines yet in the process of my optimizations. I wanted stuff to just work first. These weeks it's about getting them to work fast. ;D

Anyway, until I happen to find the root cause here, I will force a context to be created by creating an sf::Context instance at the start of any affected native load, update or create method. It might not be the way it's meant to be used, but it works for now and hopefully does not cause any potential other problems.

Funnily enough, only textures seem to be affected. Fonts and texts work fine if created explicitly before any context or window, even though they use a texture internally. Render textures are also fine. If I find any cause for this, I will post it.

EDIT:
This also means that I can create or load textures without any trouble after creating a Font or Render texture.
« Last Edit: March 16, 2013, 08:22:11 am by pdinklag »
JSFML - The Java binding to SFML.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Thread-local sf::Context
« Reply #11 on: March 16, 2013, 09:09:22 am »
Quote
Anyway, until I happen to find the root cause here, I will force a context to be created by creating an sf::Context instance at the start of any affected native load, update or create method
Make sure that it doesn't become too slow :-\
Laurent Gomila - SFML developer

pdinklag

  • Sr. Member
  • ****
  • Posts: 330
  • JSFML Developer
    • View Profile
    • JSFML Website
Re: Thread-local sf::Context
« Reply #12 on: March 16, 2013, 09:18:00 am »
From what I read in the sources, the sf::Context constructor will barely do anything if there already is an active context on the current thread. Note that I do it natively (in C++) now, not in Java, so that overhead is ruled out.

Also, I think textures are most likely loaded before entering a rendering loop or something similar, so this should not affect live performance.
JSFML - The Java binding to SFML.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Thread-local sf::Context
« Reply #13 on: March 16, 2013, 11:55:29 am »
Quote
From what I read in the sources, the sf::Context constructor will barely do anything if there already is an active context on the current thread
No, it really creates a context ;)

There is a function to create a context only if there's none in the current thread (ensureGlContext -- you probably won't be able to access it from outside SFML), but... this is exactly what SFML already does ;)
« Last Edit: March 16, 2013, 11:59:47 am by Laurent »
Laurent Gomila - SFML developer