Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SFML] MultiThreading z biblioteką

Ostatnio zmodyfikowano 2012-04-22 20:21
Autor Wiadomość
Admixior
Temat założony przez niniejszego użytkownika
[SFML] MultiThreading z biblioteką
» 2012-04-22 19:17:32
Witam.
Pisza aplikację która będzie robiła coś takiego...
1 thread: wyświetla obraz i zajmuje się innymi sprawami kosmetycznymi.
2 thread: pobiera obraz z kamery i zapisuje go do sf::Image.

Globalnie są zdefiniowane 2 obiekty sf::Image obraz[2];
Gdy w 1 threadzie wyświetlam obraz[0]; w 2 threadzie chcę ładować obraz[1];.
Niestety podczas ładowania w 2 threadzie obraz[1].LoadFromPixels(...); w 1 threadzie wykonuje się m.in. funkcja wnd.Display();
I oczywiście wychodzi błąd, że próbuje uzyskać dostęp do nie istniejącego adresu. Nie wiem czy to jest wina OpenGL-a czy moja.
W innym projekcie próbowałem robić w ten sam sposób aż w końcu zrezygnowałem. Ale tutaj takie coś jest nie możliwe.
Co mogę zrobić żeby nie wychodził błąd.

PS. W razie "w" mogę podrzucić kod.
P-55016
DejaVu
» 2012-04-22 19:28:17
Poczytaj o sekcjach krytycznych. W trakcie kopiowania obrazka, dostęp do niego powinien mieć tylko i wyłącznie jeden wątek.

Czyli: thread renderujący powinien:
  • wejść w sekcję krytyczną
  • skopiować obrazek
  • wyjść z sekcji krytycznej
  • renderować skopiowany obrazek

Thread generujący obrazek (kamera) powinien:
  • stworzyć obrazek
  • wejść w sekcję krytyczną
  • skopiować obrazek
  • wyjść z sekcji krytycznej

Czyli:
C/C++
sf::Image imageCamera; //to może być zmienna lokalna (wykorzystujesz do generowania obrazka z kamery)
sf::Image imageTransfer; //to 'musi być' zmienna globalna - wszelkie operacje na tym obrazku muszą odbywać się w sekcjach krytycznych
sf::Image imageRenderer; //to może być również zmienna lokalna (wykorzystujesz do renderowania obrazka)
P-55019
Admixior
Temat założony przez niniejszego użytkownika
» 2012-04-22 19:49:43
Właściwie nie wiem czy tak można jak ja zrobiłem ale po części oddaje sens.
Obrazki są na przemian używane w threadzie. Nie wiem czy tak można. Ale wg poniższego kodu nigdy nie powinno się zdarzyć że naraz obie funkcje mają dostęp do jednego obrazka.

Zamieściłem kod bo mogłem też ja coś zepsuć.

C/C++
//globalnie
int zrobione =- 1; //nr obrazka ostatnio zrobionego w indeksowaniu od 0
Image obrazek[ 2 ]; //obrazki

//funkcja renderująca
int nr_obrazu =- 1; //nr obrazka aktualnie używanego
Sprite kamera; //wyswietla obraz
for( bool cont = true; cont && wnd.IsOpened(); )
{
    while( wnd.GetEvent( zdarzenie ) )
    {
        if( zdarzenie.Type == Event::MouseButtonPressed )
        {
            cont = false;
        }
        else if( zdarzenie.Type == sf::Event::Closed )
        {
            wnd.Close();
        }
    }
   
    //wnd.Clear();
    if( nr_obrazu != zrobione )
    {
        kamera.SetImage( obraz[ nr_obrazu = zaladowane ] );
        zrobione =- 1;
    }
    wnd.Draw( kamera );
    wnd.Display();
}
//funkcja pobierająca obraz z kamery

UINT LadowanieObrazuZKamery( LPVOID * cp )
{
    int CAMERA_WIDTH = 1600, CAMERA_HEIGHT = 900;
    cv::VideoCapture cap( 0 ); // open the default camera
    if( !cap.isOpened() ) // check if we succeeded
    {
        obraz_z_kamery = false;
        return - 1;
    }
    else
    {
        cap.set( CV_CAP_PROP_FRAME_WIDTH, CAMERA_WIDTH );
        cap.set( CV_CAP_PROP_FRAME_HEIGHT, CAMERA_HEIGHT );
        CAMERA_WIDTH = cap.get( CV_CAP_PROP_FRAME_WIDTH );
        CAMERA_HEIGHT = cap.get( CV_CAP_PROP_FRAME_HEIGHT );
        cap.set( CV_CAP_PROP_FPS, 25 );
        cap.set( CV_CAP_PROP_CONVERT_RGB, 0 );
        obraz_z_kamery = true;
    }
    char * rgba_data = new char[ CAMERA_HEIGHT * CAMERA_WIDTH * 4 ];
    cv::Mat image_cam;
    kamera.SetScale( 1024.f / CAMERA_WIDTH, 768.f / CAMERA_HEIGHT );
   
    int x = 0; //aktualnie robione
    while( !stop_all )
    {
        while( zaladowane !=- 1 ) sf::Sleep( 0.01f ); //czekamy aż funkcja renderująca zmieni sobie obraz
       
        if( x == 2 ) x = 0;
       
        cap.read( image_cam );
        conv_rgb2rgba(( uchar * ) image_cam.data, rgba_data, CAMERA_HEIGHT * CAMERA_WIDTH );
        obraz[ x ].LoadFromPixels( CAMERA_WIDTH, CAMERA_HEIGHT,( Uint8 * ) rgba_data ); //ładujemy obraz do zrobionego
        zaladowane = x; //zapisujemy że już zrobione
        x++; //od teraz robimy na następnym elemencie
    }
    delete[] rgba_data;
    return 0;
}
P-55027
DejaVu
» 2012-04-22 19:54:12
Nie ma takiej opcji żeby działał Ci kod prawidłowo bez sekcji krytycznych.
P-55030
Admixior
Temat założony przez niniejszego użytkownika
» 2012-04-22 20:03:40
heh... A jednak znalazłem błąd.
W funkcji renderującej zamiast

if(nr_obrazu!=zaladowane)

powinno być
if(nr_obrazu!=zaladowane && zaladowane!=-1)

Dlaczego? Wystarczy zobaczyć na ciało if'a.

Funkcja pobierająca obraz z kamery trwa trochę dłużej więc próbowało odwołać się do obraz[-1].
Kolejny głupi błąd związany z tym projektem. ARGH....
P-55032
DejaVu
» 2012-04-22 20:13:25
Nadal podtrzymuję swoją wersję :)

PS. Spróbuj przeciągnąć okno aplikacji ;p
P-55033
Elaine
» 2012-04-22 20:21:51
Nie ma takiej opcji żeby działał Ci kod prawidłowo bez sekcji krytycznych.
Jeśli kopiować wskaźnik do obrazka, a nie cały obrazek, to sekcja krytyczna nie będzie potrzebna, wystarczy CAS. Martwić się wtedy można o muteks na new i delete, ale one zajmują znacznie mniej czasu niż kopiowanie dużej tablicy, w przypadku kopiowania obrazka i tak mogą zostać wywołane, a w dobrym wielowątkowym alokatorze (do których HeapAlloc się nie zalicza) walka o ten muteks nie będzie problemem.
P-55035
« 1 »
  Strona 1 z 1