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

Author Topic: Multiple threads with sf::Context and sf::Image  (Read 5795 times)

0 Members and 1 Guest are viewing this topic.

iwn

  • Newbie
  • *
  • Posts: 17
    • View Profile
Multiple threads with sf::Context and sf::Image
« on: May 26, 2011, 08:04:33 pm »
Greetings,

I've been beating my head against what I believe to be an OpenGL/threading issue for a couple days, and I'm hoping that someone might be able to help explain the behavior I'm seeing.

Code: [Select]

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

// CONFIG

#define THREADS 2                // number of threads to run
#define IMAGES 100               // number of sf::Images to create/delete during each thread's run
#define SYNC_THREAD 0            // when 1, protect sf::Image::Image(int,int) from being called simultaneously by two different threads
#define SYNC_CONTEXT 0           // when 1, also protect sf::Context::Context() from being called simultaneously by two different threads (only effective when SYNC_THREAD=1)
#define SLEEP_AFTER_DELETE 0     // when 1, sleep for a short while (SLEEP_TIME) after deleting the new sf::Image
#define SLEEP_TIME 0.01          // time to sleep after deleting the sf::Image in seconds


// SUMMARY
//
// For THREADS = 1:
// No GL Errors. No segfault. Appears to run fine (except for memory consumption,
// see below).
//
// For THREADS >= 2:
// Case | SYNC_THREAD | SYNC_CONTEXT | Result
// -----+-------------+--------------+--------------------------------------------
// 1    | 0           | 0            | Many GL Errors -- GL_INVALID_OPERATION
//      |             |              | and "Failed to create image, its internal
//      |             |              | size is too high (nxn)" where n is either
//      |             |              | 100 or 128. No segfault.
// -----+-------------+--------------+--------------------------------------------
// 2    | 0           | 1            | - (effectively same as Case 1)
// -----+-------------+--------------+--------------------------------------------
// 3    | 1           | 0            | No GL Errors. Segfault after 30-60 seconds.
// -----+-------------+--------------+--------------------------------------------
// 4    | 1           | 1            | No GL Errors. No segfault. Appears to run
//      |             |              | fine (except for memory consumption, see
//      |             |              | below).
// -----+-------------+--------------+--------------------------------------------
//
// In all cases, the memory footprint of the process grows fairly rapidly, reaching
// a few hundred MB within a minute or so. For Case 1 (SYNC_THREAD=0/SYNC_CONTEXT=0)
// only, when SLEEP_AFTER_DELETE is 1 the memory footprint does not change, but we
// still get GL errors (though they do appear less frequently).
//
// Platform: Windows 7 64-bit
// Graphics card: ATI Radeon 4800 (1GB)
// Driver: Catalyst 11.5a
// OpenGL: 6.14.10.1152

//////////////


// Mutex used to synchronize threads
sf::Mutex threadMutex;

class CreateImageThread : public sf::Thread {
    public:
        CreateImageThread() :
            ready(false)
        {
            // ctor
        }
        void Run() {

#if SYNC_THREAD
#if SYNC_CONTEXT
            // Lock the thread mutex first, then create our Context.
            // This protects sf::Context::Context() from being called simultaneously.
            sf::Lock lock(threadMutex);
            sf::Context context;
#else
            // Create the context first, then lock the thread mutex. This allows
            // sf::Context::Context() to be called simultaneously.
            sf::Context context;
            sf::Lock lock(threadMutex);
#endif
#endif

            // Create some blank images, then delete them.
            for (int i = 0; i < IMAGES; i++) {
                sf::Image* image = new sf::Image(100, 100);
                delete image;

#if SLEEP_AFTER_DELETE
                // Sleep for a short while
                sf::Sleep(SLEEP_TIME);
#endif

            }

            // Indicate to the main thread that we're done
            ready = true;
        }

        // Ready flag. We'll set this to true when the thread has finished it's run.
        bool ready;
};

int main() {
    sf::RenderWindow app;
    app.Create(sf::VideoMode(800, 600, 32), "Test", sf::Style::Close);

    // Our thread array
    std::vector<CreateImageThread*> thread(THREADS);

    while (app.IsOpened()) {
        int j = 0;
        for (std::vector<CreateImageThread*>::iterator i = thread.begin(); i != thread.end(); ++i, j++) {
            // If the thread slot is empty...
            if (*i == NULL) {
                // ... create a new thread.
                std::cout << "Launching " << j << "..." << std::endl;
                *i = new CreateImageThread();
                (*i)->Launch();

            // Or if the thread slot is non-empty and ready to be recycled...
            } else if ((*i)->ready) {
                // ... delete the thread.
                std::cout << "Ready " << j << std::endl;
                (*i)->Wait();
                delete *i;
                *i = NULL;
            }
        }

        sf::Event Event;
        while (app.GetEvent(Event))
        {
            // Exit on window close
            if (Event.Type == sf::Event::Closed) {
                app.Close();
            }
        }
    }

    return 0;
}


Basically, I'm trying to run two background threads in addition to the main thread, each of which is working with sf::Images. Reading through the forums, I found several posts suggesting that issues relating to Images in background threads can be solved by instantiating a sf::Context within the thread, for example:

http://www.sfml-dev.org/forum/viewtopic.php?p=20764#20764
http://www.sfml-dev.org/forum/viewtopic.php?p=12123#12123
http://www.sfml-dev.org/forum/viewtopic.php?p=18280#18280

However, I still observe a couple issues, even with the sf::Context.

In my first attempt to get this right (Case 1, in the code), neither sf::Image::Image(int,int) nor sf::Context::Context() was protected by a mutex, and the program ran, but produced many GL_INVALID_OPERATION errors along with "Failed to create image, its internal size is too high (NxN)" (where N is either 100 or 128 -- note I only create images at 100x100).

After seeing this, I figured that there may be some GL critical section inside sf::Image::Image(int,int) that needed protection by a user mutex. With a mutex protecting the thread's image creation loop (Case 2), the program ran with no GL errors, but it ended in a segfault after about 30-60 seconds.

I then tried to protect the thread's entire Run() method, including the calls to sf::Context::Context() and sf::Image::Image(int,int), using a mutex (Case 4). In this case, I saw no GL errors and no segfault, but I did still notice the excessive memory consumption, as noted below.

In all cases, the memory footprint of the process (as viewed in Task Manager) expands rapidly, reaching several hundred megabytes within a minute or so. In the sample code, the images are deleted immediately after they're created, so I would not expect the size of the process to grow much, if at all. Interestingly, by adding a short Sleep() following the image deletion, Case 1 will run (as described) with an absolutely static memory footprint.

I'm using SFML 1.6 on Windows 7 with MinGW32, an ATI Radeon 4800 graphics card, and the latest Catalyst drivers.

Getting down to it, I'm interested to know:

1) Am I using sf::Context and sf::Image correctly in the background threads? If so, what is causing the GL errors?

2) Why does the program's memory footprint grow, when all Images are being deleted immediately after creation? (And why might adding a Sleep() solve the problem in one case?)

And, while I seen some posts regarding the overhaul of GL context handling in SFML2, and while moving to SFML2 may ultimately be the best solution, I'd be interested in knowing what's going on here in 1.6 anyway, if that's at all possible.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Multiple threads with sf::Context and sf::Image
« Reply #1 on: May 26, 2011, 09:45:24 pm »
I'm afraid I won't be able to give you clear answers. I'm not maintaining SFML 1.6 anymore, so all I can tell you is that context handling is not 100% robust and is probably not thread-safe.

SFML 2 should fix all this stuff.
Laurent Gomila - SFML developer

iwn

  • Newbie
  • *
  • Posts: 17
    • View Profile
Multiple threads with sf::Context and sf::Image
« Reply #2 on: May 26, 2011, 09:58:01 pm »
I was afraid that might be the case. Can you at least clarify how 1.6 is intended to be used with threads? Or, if THREADS is set to 1 in the above code, would you expect it to work correctly?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Multiple threads with sf::Context and sf::Image
« Reply #3 on: May 26, 2011, 11:13:11 pm »
Yes, the above code should work.
Laurent Gomila - SFML developer

iwn

  • Newbie
  • *
  • Posts: 17
    • View Profile
Multiple threads with sf::Context and sf::Image
« Reply #4 on: May 27, 2011, 12:48:35 am »
Thanks, Laurent. I have a followup to this.

I reworked the code for SFML2 as you suggested, and the GL errors did disappear. However, I'm still seeing issues with memory consumption. I've reduced it down to the following code, which steadily increases it's memory footprint with each call to sf::Context::Context. I thought it might be related to accumulating thread overhead, but when setting NULL_RUN=1, the footprint stays relatively static. I see similar behavior on both Windows 7 / ATI Radeon 4800 and Linux / Intel 855GM  using the latest git master.

Code: [Select]

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

#define NULL_RUN 0  // when 1, skip context creation

class ContextThread : public sf::Thread {
    public:
        ContextThread() :
            sf::Thread(&ContextThread::Run, this)
        {
            // ctor
        }
        void Run() {
#if !NULL_RUN
            sf::Context context;
#endif
        }
};

int main() {
    sf::RenderWindow app;
    app.Create(sf::VideoMode(800, 600, 32), "Test", sf::Style::Close);

    while (app.IsOpened()) {
        ContextThread* thread = new ContextThread();
        thread->Launch();
        thread->Wait();
        delete thread;

        sf::Event Event;
        while (app.PollEvent(Event))
        {
            // Exit on window close
            if (Event.Type == sf::Event::Closed) {
                app.Close();
            }
        }
    }

    return 0;
}


Am I still using the sf::Context correctly here? If so, is this behavior  expected?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Multiple threads with sf::Context and sf::Image
« Reply #5 on: May 27, 2011, 07:40:17 am »
What is your OS?

You're using sf::Context correctly (there's nothing wrong instanciating a class than can be instnaciated ;)) -- but you don't need it anymore in SFML 2.
Laurent Gomila - SFML developer

iwn

  • Newbie
  • *
  • Posts: 17
    • View Profile
Multiple threads with sf::Context and sf::Image
« Reply #6 on: May 27, 2011, 07:45:33 am »
I'm running Windows 7, but I see similar behavior on Debian Linux as well.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Multiple threads with sf::Context and sf::Image
« Reply #7 on: May 27, 2011, 07:53:56 am »
So maybe you could run valgrind to know where the leak comes from.
Laurent Gomila - SFML developer

iwn

  • Newbie
  • *
  • Posts: 17
    • View Profile
Multiple threads with sf::Context and sf::Image
« Reply #8 on: May 27, 2011, 08:15:29 am »
Thanks for clarifying the usage of sf::Context in SFML2. I revised the last sample by removing the unnecessary sf::Context and replacing it with the image creation loop from my first post.

Code: [Select]

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

#define NULL_RUN 0  // when 1, skip image creation loop

class ContextThread : public sf::Thread {
    public:
        ContextThread() :
            sf::Thread(&ContextThread::Run, this)
        {
            // ctor
        }
        void Run() {
#if !NULL_RUN
            for (int i = 0; i < 10; i++) {
                sf::Image* image = new sf::Image();
                image->Create(100, 100);
                delete image;
            }
#endif
        }
};

int main() {
    sf::RenderWindow app;
    app.Create(sf::VideoMode(800, 600, 32), "Test", sf::Style::Close);

    while (app.IsOpened()) {
        ContextThread* thread = new ContextThread();
        thread->Launch();
        thread->Wait();
        delete thread;

        sf::Event Event;
        while (app.PollEvent(Event))
        {
            // Exit on window close
            if (Event.Type == sf::Event::Closed) {
                app.Close();
            }
        }
    }

    return 0;
}


When I run this code on Windows 7, the process ramps up to about 350MB working set and then maintains that. After about 60 seconds, I get a run of GL errors, which recur every 30 to 60 seconds.

  An internal OpenGL call failed in Image.cpp (XXX) : GL_INVALID_OPERATION, the specified operation is not allowed in the current state

On Debian Linux I'm seeing similar behavior with regard to memory, but no GL errors. Instead, it crashes after a few minutes with:

  Maximum number of clients reachedFailed to connect to the X server while trying to get the desktop video modes
  Maximum number of clients reachedSegmentation fault

iwn

  • Newbie
  • *
  • Posts: 17
    • View Profile
Multiple threads with sf::Context and sf::Image
« Reply #9 on: May 27, 2011, 08:32:35 am »
Here's the valgrind output for the most recent sample code, after running for a couple minutes.

valgrind --leak-check=yes ./testcontext4

Code: [Select]

==28864== Memcheck, a memory error detector
==28864== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==28864== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==28864== Command: ./testcontext4
==28864==
==28864==
==28864== HEAP SUMMARY:
==28864==     in use at exit: 777,424,068 bytes in 116,874 blocks
==28864==   total heap usage: 174,908 allocs, 58,034 frees, 869,300,595 bytes allocated
==28864==
==28864== 124 bytes in 1 blocks are definitely lost in loss record 280 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x473ABC2: ??? (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473AC63: ??? (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473A5A3: xcb_connect_to_display_with_auth_info (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473A8DB: xcb_connect (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x456BD72: _XConnectXCB (in /usr/lib/libX11.so.6.3.0)
==28864==    by 0x455BC16: XOpenDisplay (in /usr/lib/libX11.so.6.3.0)
==28864==    by 0x4101523: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:47)
==28864==    by 0x40FA381: sf::priv::GlContext::GlobalInit() (GlContext.cpp:108)
==28864==    by 0x40FB90A: sf::GlResource::GlResource() (GlResource.cpp:57)
==28864==    by 0x40FE41D: sf::Window::Window() (Window.cpp:53)
==28864==    by 0x4070565: sf::RenderWindow::RenderWindow() (RenderWindow.cpp:34)
==28864==
==28864== 124 bytes in 1 blocks are definitely lost in loss record 281 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x473ABC2: ??? (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473AC63: ??? (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473A5A3: xcb_connect_to_display_with_auth_info (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473A8DB: xcb_connect (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x456BD72: _XConnectXCB (in /usr/lib/libX11.so.6.3.0)
==28864==    by 0x455BC16: XOpenDisplay (in /usr/lib/libX11.so.6.3.0)
==28864==    by 0x4102B1D: sf::priv::VideoModeImpl::GetDesktopMode() (VideoModeImpl.cpp:122)
==28864==    by 0x40FBDD7: sf::VideoMode::GetDesktopMode() (VideoMode.cpp:60)
==28864==    by 0x4101637: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==28864==    by 0x40FA381: sf::priv::GlContext::GlobalInit() (GlContext.cpp:108)
==28864==    by 0x40FB90A: sf::GlResource::GlResource() (GlResource.cpp:57)
==28864==
==28864== 124 bytes in 1 blocks are definitely lost in loss record 282 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x473ABC2: ??? (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473AC63: ??? (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473A5A3: xcb_connect_to_display_with_auth_info (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473A8DB: xcb_connect (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x456BD72: _XConnectXCB (in /usr/lib/libX11.so.6.3.0)
==28864==    by 0x455BC16: XOpenDisplay (in /usr/lib/libX11.so.6.3.0)
==28864==    by 0x4101523: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:47)
==28864==    by 0x40FA541: sf::priv::GlContext::New() (GlContext.cpp:145)
==28864==    by 0x40FA2BB: (anonymous namespace)::GetInternalContext() (GlContext.cpp:90)
==28864==    by 0x40FA798: sf::priv::GlContext::SetActive(bool) (GlContext.cpp:227)
==28864==    by 0x40FA3AD: sf::priv::GlContext::GlobalInit() (GlContext.cpp:114)
==28864==
==28864== 124 bytes in 1 blocks are definitely lost in loss record 283 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x473ABC2: ??? (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473AC63: ??? (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473A5A3: xcb_connect_to_display_with_auth_info (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473A8DB: xcb_connect (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x456BD72: _XConnectXCB (in /usr/lib/libX11.so.6.3.0)
==28864==    by 0x455BC16: XOpenDisplay (in /usr/lib/libX11.so.6.3.0)
==28864==    by 0x41036F9: sf::priv::WindowImplX11::WindowImplX11(sf::VideoMode, std::string const&, unsigned long) (WindowImplX11.cpp:114)
==28864==    by 0x40FF132: sf::priv::WindowImpl::New(sf::VideoMode, std::string const&, unsigned long) (WindowImpl.cpp:58)
==28864==    by 0x40FE82C: sf::Window::Create(sf::VideoMode, std::string const&, unsigned long, sf::ContextSettings const&) (Window.cpp:126)
==28864==    by 0x8048FC7: main (testcontext4.cpp:27)
==28864==
==28864== 160 bytes in 1 blocks are possibly lost in loss record 290 of 678
==28864==    at 0x4023796: calloc (vg_replace_malloc.c:467)
==28864==    by 0x401103B: _dl_allocate_tls (dl-tls.c:300)
==28864==    by 0x43BC5A0: pthread_create@@GLIBC_2.1 (allocatestack.c:579)
==28864==    by 0x411980B: sf::priv::ThreadImpl::ThreadImpl(sf::Thread*) (ThreadImpl.cpp:41)
==28864==    by 0x41194CF: sf::Thread::Launch() (Thread.cpp:52)
==28864==    by 0x8049018: main (testcontext4.cpp:31)
==28864==
==28864== 282 bytes in 1 blocks are definitely lost in loss record 322 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x4508253: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E8063: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x450C624: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E84E1: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E54B2: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E55E4: glXGetConfig (in /usr/lib/libGL.so.1.2)
==28864==    by 0x4101C67: sf::priv::GlxContext::CreateContext(sf::priv::GlxContext*, unsigned int, sf::ContextSettings const&) (GlxContext.cpp:197)
==28864==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==28864==    by 0x40FA381: sf::priv::GlContext::GlobalInit() (GlContext.cpp:108)
==28864==    by 0x40FB90A: sf::GlResource::GlResource() (GlResource.cpp:57)
==28864==    by 0x40FE41D: sf::Window::Window() (Window.cpp:53)
==28864==
==28864== 282 bytes in 1 blocks are definitely lost in loss record 323 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x4508253: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E8063: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x450C624: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E84E1: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E54B2: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E55E4: glXGetConfig (in /usr/lib/libGL.so.1.2)
==28864==    by 0x4101C67: sf::priv::GlxContext::CreateContext(sf::priv::GlxContext*, unsigned int, sf::ContextSettings const&) (GlxContext.cpp:197)
==28864==    by 0x410171D: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*, sf::ContextSettings const&, sf::priv::WindowImpl const*, unsigned int) (GlxContext.cpp:80)
==28864==    by 0x40FA5C1: sf::priv::GlContext::New(sf::ContextSettings const&, sf::priv::WindowImpl const*, unsigned int) (GlContext.cpp:159)
==28864==    by 0x40FE850: sf::Window::Create(sf::VideoMode, std::string const&, unsigned long, sf::ContextSettings const&) (Window.cpp:129)
==28864==    by 0x8048FC7: main (testcontext4.cpp:27)
==28864==
==28864== 282 bytes in 1 blocks are definitely lost in loss record 324 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x4508253: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E8063: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x450C624: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E84E1: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E54B2: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E55E4: glXGetConfig (in /usr/lib/libGL.so.1.2)
==28864==    by 0x4101C67: sf::priv::GlxContext::CreateContext(sf::priv::GlxContext*, unsigned int, sf::ContextSettings const&) (GlxContext.cpp:197)
==28864==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==28864==    by 0x40FA541: sf::priv::GlContext::New() (GlContext.cpp:145)
==28864==    by 0x40FA2BB: (anonymous namespace)::GetInternalContext() (GlContext.cpp:90)
==28864==    by 0x40FA798: sf::priv::GlContext::SetActive(bool) (GlContext.cpp:227)
==28864==
==28864== 10,404 bytes in 51 blocks are possibly lost in loss record 524 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x44E3E6A: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E7EA8: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E8191: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x450C624: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E84E1: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E54B2: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E55E4: glXGetConfig (in /usr/lib/libGL.so.1.2)
==28864==    by 0x4101C67: sf::priv::GlxContext::CreateContext(sf::priv::GlxContext*, unsigned int, sf::ContextSettings const&) (GlxContext.cpp:197)
==28864==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==28864==    by 0x40FA541: sf::priv::GlContext::New() (GlContext.cpp:145)
==28864==    by 0x40FA2BB: (anonymous namespace)::GetInternalContext() (GlContext.cpp:90)
==28864==
==28864== 11,408 bytes in 92 blocks are definitely lost in loss record 532 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x473ABC2: ??? (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473AC63: ??? (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473A5A3: xcb_connect_to_display_with_auth_info (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473A8DB: xcb_connect (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x456BD72: _XConnectXCB (in /usr/lib/libX11.so.6.3.0)
==28864==    by 0x455BC16: XOpenDisplay (in /usr/lib/libX11.so.6.3.0)
==28864==    by 0x4101523: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:47)
==28864==    by 0x40FA541: sf::priv::GlContext::New() (GlContext.cpp:145)
==28864==    by 0x40FA2BB: (anonymous namespace)::GetInternalContext() (GlContext.cpp:90)
==28864==    by 0x40FA4F5: sf::priv::GlContext::EnsureContext() (GlContext.cpp:138)
==28864==    by 0x40FB929: sf::GlResource::GlResource() (GlResource.cpp:64)
==28864==
==28864== 11,532 bytes in 93 blocks are definitely lost in loss record 533 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x473ABC2: ??? (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473AC63: ??? (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473A5A3: xcb_connect_to_display_with_auth_info (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x473A8DB: xcb_connect (in /usr/lib/libxcb.so.1.1.0)
==28864==    by 0x456BD72: _XConnectXCB (in /usr/lib/libX11.so.6.3.0)
==28864==    by 0x455BC16: XOpenDisplay (in /usr/lib/libX11.so.6.3.0)
==28864==    by 0x4102B1D: sf::priv::VideoModeImpl::GetDesktopMode() (VideoModeImpl.cpp:122)
==28864==    by 0x40FBDD7: sf::VideoMode::GetDesktopMode() (VideoMode.cpp:60)
==28864==    by 0x4101637: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==28864==    by 0x40FA541: sf::priv::GlContext::New() (GlContext.cpp:145)
==28864==    by 0x40FA2BB: (anonymous namespace)::GetInternalContext() (GlContext.cpp:90)
==28864==
==28864== 13,056 (204 direct, 12,852 indirect) bytes in 1 blocks are definitely lost in loss record 540 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x44E3E6A: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E7EA8: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E8191: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x450C624: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E84E1: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E54B2: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E55E4: glXGetConfig (in /usr/lib/libGL.so.1.2)
==28864==    by 0x4101C67: sf::priv::GlxContext::CreateContext(sf::priv::GlxContext*, unsigned int, sf::ContextSettings const&) (GlxContext.cpp:197)
==28864==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==28864==    by 0x40FA381: sf::priv::GlContext::GlobalInit() (GlContext.cpp:108)
==28864==    by 0x40FB90A: sf::GlResource::GlResource() (GlResource.cpp:57)
==28864==
==28864== 13,056 (204 direct, 12,852 indirect) bytes in 1 blocks are definitely lost in loss record 541 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x44E3E6A: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E7EA8: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E8191: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x450C624: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E84E1: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E54B2: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E55E4: glXGetConfig (in /usr/lib/libGL.so.1.2)
==28864==    by 0x4101C67: sf::priv::GlxContext::CreateContext(sf::priv::GlxContext*, unsigned int, sf::ContextSettings const&) (GlxContext.cpp:197)
==28864==    by 0x410171D: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*, sf::ContextSettings const&, sf::priv::WindowImpl const*, unsigned int) (GlxContext.cpp:80)
==28864==    by 0x40FA5C1: sf::priv::GlContext::New(sf::ContextSettings const&, sf::priv::WindowImpl const*, unsigned int) (GlContext.cpp:159)
==28864==    by 0x40FE850: sf::Window::Create(sf::VideoMode, std::string const&, unsigned long, sf::ContextSettings const&) (Window.cpp:129)
==28864==
==28864== 25,944 bytes in 92 blocks are definitely lost in loss record 568 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x4508253: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E8063: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x450C624: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E84E1: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E54B2: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E55E4: glXGetConfig (in /usr/lib/libGL.so.1.2)
==28864==    by 0x4101C67: sf::priv::GlxContext::CreateContext(sf::priv::GlxContext*, unsigned int, sf::ContextSettings const&) (GlxContext.cpp:197)
==28864==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==28864==    by 0x40FA541: sf::priv::GlContext::New() (GlContext.cpp:145)
==28864==    by 0x40FA2BB: (anonymous namespace)::GetInternalContext() (GlContext.cpp:90)
==28864==    by 0x40FA4F5: sf::priv::GlContext::EnsureContext() (GlContext.cpp:138)
==28864==
==28864== 26,112 (204 direct, 25,908 indirect) bytes in 1 blocks are definitely lost in loss record 569 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x44E3E6A: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E7EA8: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E81D3: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x450C624: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E84E1: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E54B2: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E55E4: glXGetConfig (in /usr/lib/libGL.so.1.2)
==28864==    by 0x4101C67: sf::priv::GlxContext::CreateContext(sf::priv::GlxContext*, unsigned int, sf::ContextSettings const&) (GlxContext.cpp:197)
==28864==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==28864==    by 0x40FA381: sf::priv::GlContext::GlobalInit() (GlContext.cpp:108)
==28864==    by 0x40FB90A: sf::GlResource::GlResource() (GlResource.cpp:57)
==28864==
==28864== 26,112 (204 direct, 25,908 indirect) bytes in 1 blocks are definitely lost in loss record 570 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x44E3E6A: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E7EA8: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E81D3: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x450C624: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E84E1: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E54B2: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E55E4: glXGetConfig (in /usr/lib/libGL.so.1.2)
==28864==    by 0x4101C67: sf::priv::GlxContext::CreateContext(sf::priv::GlxContext*, unsigned int, sf::ContextSettings const&) (GlxContext.cpp:197)
==28864==    by 0x410171D: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*, sf::ContextSettings const&, sf::priv::WindowImpl const*, unsigned int) (GlxContext.cpp:80)
==28864==    by 0x40FA5C1: sf::priv::GlContext::New(sf::ContextSettings const&, sf::priv::WindowImpl const*, unsigned int) (GlContext.cpp:159)
==28864==    by 0x40FE850: sf::Window::Create(sf::VideoMode, std::string const&, unsigned long, sf::ContextSettings const&) (Window.cpp:129)
==28864==
==28864== 60,996 bytes in 299 blocks are possibly lost in loss record 593 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x44E3E6A: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E7EA8: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E81D3: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x450C624: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E84E1: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E54B2: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E55E4: glXGetConfig (in /usr/lib/libGL.so.1.2)
==28864==    by 0x4101C67: sf::priv::GlxContext::CreateContext(sf::priv::GlxContext*, unsigned int, sf::ContextSettings const&) (GlxContext.cpp:197)
==28864==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==28864==    by 0x40FA541: sf::priv::GlContext::New() (GlContext.cpp:145)
==28864==    by 0x40FA2BB: (anonymous namespace)::GetInternalContext() (GlContext.cpp:90)
==28864==
==28864== 1,191,564 (18,972 direct, 1,172,592 indirect) bytes in 93 blocks are definitely lost in loss record 640 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x44E3E6A: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E7EA8: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E8191: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x450C624: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E84E1: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E54B2: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E55E4: glXGetConfig (in /usr/lib/libGL.so.1.2)
==28864==    by 0x4101C67: sf::priv::GlxContext::CreateContext(sf::priv::GlxContext*, unsigned int, sf::ContextSettings const&) (GlxContext.cpp:197)
==28864==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==28864==    by 0x40FA541: sf::priv::GlContext::New() (GlContext.cpp:145)
==28864==    by 0x40FA2BB: (anonymous namespace)::GetInternalContext() (GlContext.cpp:90)
==28864==
==28864== 2,367,420 (18,768 direct, 2,348,652 indirect) bytes in 92 blocks are definitely lost in loss record 653 of 678
==28864==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==28864==    by 0x44E3E6A: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E7EA8: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E81D3: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x450C624: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E84E1: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E54B2: ??? (in /usr/lib/libGL.so.1.2)
==28864==    by 0x44E55E4: glXGetConfig (in /usr/lib/libGL.so.1.2)
==28864==    by 0x4101C67: sf::priv::GlxContext::CreateContext(sf::priv::GlxContext*, unsigned int, sf::ContextSettings const&) (GlxContext.cpp:197)
==28864==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==28864==    by 0x40FA541: sf::priv::GlContext::New() (GlContext.cpp:145)
==28864==    by 0x40FA2BB: (anonymous namespace)::GetInternalContext() (GlContext.cpp:90)
==28864==
==28864== LEAK SUMMARY:
==28864==    definitely lost: 88,782 bytes in 473 blocks
==28864==    indirectly lost: 3,598,764 bytes in 17,641 blocks
==28864==      possibly lost: 71,560 bytes in 351 blocks
==28864==    still reachable: 773,664,962 bytes in 98,409 blocks
==28864==         suppressed: 0 bytes in 0 blocks
==28864== Reachable blocks (those to which a pointer was found) are not shown.
==28864== To see them, rerun with: --leak-check=full --show-reachable=yes
==28864==
==28864== For counts of detected and suppressed errors, rerun with: -v
==28864== ERROR SUMMARY: 19 errors from 19 contexts (suppressed: 89 from 12)

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Multiple threads with sf::Context and sf::Image
« Reply #10 on: May 27, 2011, 08:52:28 am »
Half of the leaks are due to glXGetConfig, the other half is XOpenDisplay.

But:
- glXGetConfig doesn't return anything new, so there's nothing to free
- each XOpenDisplay() has its matching XCloseDisplay()
Laurent Gomila - SFML developer

iwn

  • Newbie
  • *
  • Posts: 17
    • View Profile
Multiple threads with sf::Context and sf::Image
« Reply #11 on: May 27, 2011, 09:04:59 am »
I'm not that familiar with valgrind, but the summary seems to suggest that the memory consumption is due to active allocations, rather than leaks, no?

I re-ran valgrind with --leak-check=full --show-reachable=yes and it gives the three largest, reachable allocations as:

Code: [Select]

==30039== Memcheck, a memory error detector
==30039== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==30039== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==30039== Command: ./testcontext4
==30039==
==30039==
==30039== HEAP SUMMARY:
==30039==     in use at exit: 218,235,115 bytes in 32,584 blocks
==30039==   total heap usage: 50,698 allocs, 18,114 frees, 241,961,036 bytes allocated
==30039==

<snip>

==30039== 24,117,248 bytes in 23 blocks are still reachable in loss record 673 of 676
==30039==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==30039==    by 0x4C39A2C: _mesa_malloc (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4CB4C4B: _swrast_CreateContext (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4BF934F: ??? (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4508625: ??? (in /usr/lib/libGL.so.1.2)
==30039==    by 0x44E44AF: ??? (in /usr/lib/libGL.so.1.2)
==30039==    by 0x44E5BFF: glXCreateContext (in /usr/lib/libGL.so.1.2)
==30039==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==30039==    by 0x40FA541: sf::priv::GlContext::New() (GlContext.cpp:145)
==30039==    by 0x40FA2BB: (anonymous namespace)::GetInternalContext() (GlContext.cpp:90)
==30039==    by 0x40FA4F5: sf::priv::GlContext::EnsureContext() (GlContext.cpp:138)
==30039==    by 0x40FB929: sf::GlResource::GlResource() (GlResource.cpp:64)
==30039==
==30039== 35,276,544 bytes in 24 blocks are still reachable in loss record 674 of 676
==30039==    at 0x40235D0: memalign (vg_replace_malloc.c:581)
==30039==    by 0x402368E: posix_memalign (vg_replace_malloc.c:709)
==30039==    by 0x4C398DA: _mesa_align_malloc (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4C399AB: _mesa_align_calloc (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4C8E5BF: _tnl_init_vertices (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4CE79CC: _swsetup_CreateContext (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4BF9367: ??? (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4508625: ??? (in /usr/lib/libGL.so.1.2)
==30039==    by 0x44E44AF: ??? (in /usr/lib/libGL.so.1.2)
==30039==    by 0x44E5BFF: glXCreateContext (in /usr/lib/libGL.so.1.2)
==30039==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==30039==    by 0x40FA541: sf::priv::GlContext::New() (GlContext.cpp:145)
==30039==
==30039== 38,720,000 bytes in 800 blocks are still reachable in loss record 675 of 676
==30039==    at 0x40235D0: memalign (vg_replace_malloc.c:581)
==30039==    by 0x402368E: posix_memalign (vg_replace_malloc.c:709)
==30039==    by 0x4C398DA: _mesa_align_malloc (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4D61F31: _mesa_vector4f_alloc (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4C7B809: ??? (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4C7855C: _tnl_install_pipeline (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4C78268: _tnl_CreateContext (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4BF935F: ??? (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4508625: ??? (in /usr/lib/libGL.so.1.2)
==30039==    by 0x44E44AF: ??? (in /usr/lib/libGL.so.1.2)
==30039==    by 0x44E5BFF: glXCreateContext (in /usr/lib/libGL.so.1.2)
==30039==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==30039==
==30039== 51,343,544 bytes in 23 blocks are still reachable in loss record 676 of 676
==30039==    at 0x4025018: malloc (vg_replace_malloc.c:236)
==30039==    by 0x4C39A2C: _mesa_malloc (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4CB4BE6: _swrast_CreateContext (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4BF934F: ??? (in /usr/lib/dri/swrast_dri.so)
==30039==    by 0x4508625: ??? (in /usr/lib/libGL.so.1.2)
==30039==    by 0x44E44AF: ??? (in /usr/lib/libGL.so.1.2)
==30039==    by 0x44E5BFF: glXCreateContext (in /usr/lib/libGL.so.1.2)
==30039==    by 0x410165A: sf::priv::GlxContext::GlxContext(sf::priv::GlxContext*) (GlxContext.cpp:62)
==30039==    by 0x40FA541: sf::priv::GlContext::New() (GlContext.cpp:145)
==30039==    by 0x40FA2BB: (anonymous namespace)::GetInternalContext() (GlContext.cpp:90)
==30039==    by 0x40FA4F5: sf::priv::GlContext::EnsureContext() (GlContext.cpp:138)
==30039==    by 0x40FB929: sf::GlResource::GlResource() (GlResource.cpp:64)
==30039==
==30039== LEAK SUMMARY:
==30039==    definitely lost: 24,512 bytes in 131 blocks
==30039==    indirectly lost: 983,280 bytes in 4,820 blocks
==30039==      possibly lost: 18,520 bytes in 91 blocks
==30039==    still reachable: 217,208,803 bytes in 27,542 blocks
==30039==         suppressed: 0 bytes in 0 blocks
==30039==
==30039== For counts of detected and suppressed errors, rerun with: -v
==30039== ERROR SUMMARY: 18 errors from 18 contexts (suppressed: 89 from 12)


To me, it looks like we're creating a number of sf::GLResources and/or sf::GLContexts that aren't going away when the thread dies, but I may be misunderstanding the way the GL code is intended to work.

Can you offer any insight here?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Multiple threads with sf::Context and sf::Image
« Reply #12 on: May 27, 2011, 09:44:19 am »
I'm not familiar with it too. What is the difference between a leak and an active allocation? What is a reachable allocation?
Laurent Gomila - SFML developer

iwn

  • Newbie
  • *
  • Posts: 17
    • View Profile
Multiple threads with sf::Context and sf::Image
« Reply #13 on: May 30, 2011, 09:32:50 pm »
When I said "active" I meant the same thing as reachable. And by reachable allocation, I believe valgrind means to say that there was a pointer somewhere in memory that addresses the allocation at the time I killed the process. Presumably, a reachable memory allocation is in use by the application and will be cleaned up later.

----

I dug a little deeper into this and I belive I've found the root cause of the memory consumption in SFML2. Consider this code sample:

Code: [Select]

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

// When running either test with main1, the program's memory footprint
// will remain more or less static.
// When running either test with main2, the program's memory footprint
// will grow rapidly until it exhausts system resources.

// CONFIG

#define TEST test1     // test to run, either test1 or test2
#define MAIN main1     // main method to use, either main1 or main2

// test1 creates a user GlContext explicitly

void test1() {
  // ** Thread is contextless
  sf::Context c1;
  // ** Thread has active context c1
} // c1 goes out of scope
  // ~Context is called for c1:
  //   myContext->~GlContext calls GetInternalContext()->SetActive(true) during
  //   c1.SetActive(false)
  //   Because we did not previously have an internal context,
  //   GetIntrnalContext() creates one, then SetActive(true) makes it active
  //   ** Thread has active internal context
  //   ** internalContexts.size() increases by 1
  // ~GlResource is called for c1:
  //   If there are no other GlResources (ex. a RenderWindow in the main
  //   thread), then
  //     We call GlContext::GlobalCleanup():
  //       ** Thread's internal context is deleted
  //       ** internalContexts.size() is 0
  //   Otherwise, there are other resources -- postpone cleanup
  //     ** internalContexts.size() remains unchanged

// test2 creates an internal GlContext implicitly

void test2() {
  // ** Thread is contextless
  sf::Image* image = new sf::Image(); // calls EnsureContext()
  // EnsureContext() calls GetInternalContext(), and because we
  // have no internal context at this point, one is created and activated
  // ** Thread has active internal context
  // ** internalContexts.size() increases by 1
  delete image;
  // image is deleted:
  // ~GlResource is called for image:
  //   If there are no other GlResources (ex. a RenderWindow in the main
  //   thread), then
  //     We call GlContext::GlobalCleanup():
  //       ** Thread's internal context is deleted
  //       ** internalContexts.size() is 0
  //   Otherwise, there are other resources -- postpone cleanup
  //     ** internalContexts.size() remains unchanged
}

// main1 runs fine with either test1 or test2

void main1() {
  // ** internalContexts.size() is 0
  while (true) {
    sf::Thread thread(&TEST);
    thread.Launch();
    // ** internalContexts.size() increases by 1 during thread's run
    thread.Wait();
    // ** internalContexts.size() returns to 0

    // When last GlResource goes out of scope we
    // call GlContext::GlobalCleanup() -- memory
    // footprint remains the same.
  }
}

// main2 continually consumes resources with either test1 or test2

void main2() {
  // ** internalContexts.size() is 0
  sf::RenderWindow window;
  // ** internalContexts.size() is 1
  while (true) {
    sf::Thread thread(&TEST);
    thread.Launch();
    // ** internalContexts.size() increases by 1 during thread's run
    thread.Wait();
    // ** internalContexts.size() remains unchanged

    // GlContext::GlobalCleanup() is not called because
    // sf::RenderWindow remains in scope and keeps
    // GlResource::count at 2 -- memory footprint
    // grows until we either run out of memory or exhaust
    // system resources (i.e. X display connections),
    // then we may get GL errors or program crash.
  }
}

int main() {
  MAIN();
  return 0;
}


Postponing the GlContext cleanup via the global internalContexts seems to be by design, and I believe I understand why this needs to be in place for certain cases where the thread terminates abnormally. But in cases like the one above, where a thread exits cleanly, and it clearly dismisses the GL resources it used during it's run, having the context linger around in internalContexts doesn't make sense (at least, to me).

I guess I'm really asking this: Why must we always have a context allocated to a thread, or rather, why is internalContext == NULL is an inappropriate state for a thread, especially when a thread starts it's life contextless? Specifically, why do we need to do this

Code: [Select]

  --- SFML/Window/GlContext.cpp:223 ---
  if (this == currentContext)
  {
    // To deactivate the context, we actually activate another one so that we make
    // sure that there is always an active context for subsequent graphics operations
    return GetInternalContext()->SetActive(true);
  }


when we could instead do something like this?

Code: [Select]

  if (this == currentContext)
  {
    currentContext = NULL;
    if (internalContext && (this != internalContext)) {
      return internalContext->SetActive(true);
    } else {
      return true;
    }
  }


As far as I can tell, this will achive nearly the same thing as the original code, except it will not, as a side affect, instantiate an internal GlContext for a thread when there wasn't one there to begin with (as happens in test1(), above). I say nearly because in the case internalContext is NULL, it won't change the true active context, via GL calls -- the thread will still technically have a context as far as GL is concerned (i.e. wglGetCurrentContext() != NULL). However, at the next interaction with a GlResource, EnsureContext() should end up calling GetInternalContext()->SetActive(true), which will create a new internal context, and activate the context at that time. Or, if there is no further interaction with a GlResource, the destructor for GlContext will make the appropriate GL calls to truly place the thread into a contextless state (i.e. wglMakeCurrent(NULL, NULL)).

Am I just plain wrong? If not, can you see any unwanted side-effects resulting from a change like this?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Multiple threads with sf::Context and sf::Image
« Reply #14 on: May 30, 2011, 10:37:37 pm »
Wow, that's a... deep analysis :)

You're right about what you've found out, and your conclusion is relevant. However there's one big problem with your solution: we really need the context to be deactivated after calling SetActive(false). If it's still active (even if internalContext is NULL) it's clearly a bug.
Laurent Gomila - SFML developer