Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: Tataina от Июль 07, 2011, 09:48



Название: Использование OpenGL во втором потоке на Qt
Отправлено: Tataina от Июль 07, 2011, 09:48
 Доброе утро!
 Уже несколько дней бьюсь над тем,чтобы перенести полностью отрисовку изображения из GUI потока в другой. Существует QGLWidget, находящийся в основном потоке, необходимо в другом потоке используя Qt и OpenGL (не QPixmap, QImage и т.д.) сделать картинку и отобразить ее в основном потоке.
Поток создается, сигнал-слотовое соединение работает. Вопрос только в том,что не понимаю как и на чем нарисовать OpenGL-командами в другом потоке, и как передать это изображение в основной!
Возможно, кто-то сталкивался с подобным.
Заранее спасибо!


Название: Re: Использование OpenGL во втором потоке на Qt
Отправлено: climber от Июль 08, 2011, 08:20
Насколько мне известно гуевый поток может быть только один. Соответсвенно в отдельный поток можно и нужно выносить только обработку данных, а сама отрисовка будет происходить в главном (гуевом) потоке после получения сигнала finished(). Для создания потока обработки данных нужно создать свой класс унаследованный от QThread. Затем переопределяется метод run() под ваши конкретные задачи. Далее запускается созданный поток start(), а в коструктор класса вызвавшего этот дочерний поток вешается
connect(thread, SIGNAL(finished()),this,SLOT(_обработчик конца выполнения потока_));
Таким образом все вычисления и обработки данных в дочернем потоке, а отрисовка в главном потоке. Как то так. Если интересно, подробнее это описано тут http://www.opennet.ru/docs/RUS/qt3_prog/x7810.html (http://www.opennet.ru/docs/RUS/qt3_prog/x7810.html)


Название: Re: Использование OpenGL во втором потоке на Qt
Отправлено: Tataina от Июль 08, 2011, 09:44
на данный момент я обнаружила статью, в которой якобы приводится пример работы многопоточного приложения.
вот она: http://doc.qt.nokia.com/qq/qq06-glimpsing.html#generaltipshttp://doc.qt.nokia.com/qq/qq06-glimpsing.html#generaltips (http://doc.qt.nokia.com/qq/qq06-glimpsing.html#generaltips)
так вот при перекладывании этой проги на Qt4 у меня даже виджет в качестве окошка не оказывается. просто черный экран в углу  mainwindow. и я совершенно определенно не нашла в этой проге изменения контекста, и самого рисования пирамидки..
может кто-то сможет помочь? могу выслать исходники программы,написанной под  Qt 4.7..


Название: Re: Использование OpenGL во втором потоке на Qt
Отправлено: twp от Июль 08, 2011, 14:50
да, было бы неплохо выложить исходники. А в одном потоке все работает ок?


Название: Re: Использование OpenGL во втором потоке на Qt
Отправлено: Tataina от Июль 08, 2011, 15:25
Класс наследника QGLWidget
Код:
#ifndef GLWIDGET_H
#define GLWIDGET_H
#include <QGLWidget>
#include "glthread.h"
class GLWidget : public QGLWidget
{

public:
    GLWidget(QWidget *parent);
    void startRendering();
    void stopRendering();

protected:
    void resizeEvent(QResizeEvent *evt);
    void paintEvent(QPaintEvent *);
    void closeEvent(QCloseEvent *evt);

    GLThread glt;
};
реализация GLWidget:
Код:
#include "glwidget.h"
#include <QResizeEvent>
GLWidget::GLWidget(QWidget *parent)
    : QGLWidget(parent),
      glt(this)
{
    setFormat(QGLFormat(QGL::DoubleBuffer | QGL::DepthBuffer));
    qglClearColor(Qt::white);
    setAutoBufferSwap(false);
    resize(320, 240);
}

void GLWidget::startRendering()
{
     glt.start();
}

void GLWidget::stopRendering()
{
    glt.stop();
    glt.wait();
}

void GLWidget::resizeEvent(QResizeEvent *evt)
{
    glt.resizeViewport(evt->size());
}

void GLWidget::paintEvent(QPaintEvent *)
{
    // Handled by the GLThread.
}

void GLWidget::closeEvent(QCloseEvent *evt)
{
    stopRendering();
    QGLWidget::closeEvent(evt);
}
Наследник QThread:
Код:
#ifndef GLTHREAD_H
#define GLTHREAD_H

#include <QThread>
#include <QSize>

class GLWidget;
class GLThread : public QThread
{
public:
    GLThread(GLWidget *glWidget);
    void resizeViewport(const QSize &size);
    void run();
    void stop();

private:
    bool doRendering;
    bool doResize;
    int w;
    int h;
    int rotAngle;
public:
    GLWidget *glw;
};
#endif // GLTHREAD_H

Реализация методов:
Код:
#include "glthread.h"
#include <QtGui>
#include <QtOpenGL>
#include "glwidget.h"
GLThread::GLThread(GLWidget *gl)
    : QThread(), glw(gl)
{
    doRendering = true;
    doResize = false;
   // glw->qglClearColor(Qt::white);
}

void GLThread::stop()
{
    doRendering = false;
}

void GLThread::resizeViewport(const QSize &size)
{
    glMatrixMode(GL_PROJECTION);
    w = size.width();
    h = size.height();
    doResize = true;
}

void GLThread::run()
{
    srand(QTime::currentTime().msec());
    rotAngle = rand() % 360;

    glw->makeCurrent();
   // glw->qglClearColor(Qt::white); // qglClearColor(Qt::white);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-5.0, 5.0, -5.0, 5.0, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glViewport(0, 0, 200, 200);
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST);

//    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 // test of painting
//    glBegin(GL_LINES);
//    glVertex2f(-0.6,-0.4);
//    glVertex2f(0.6,-0.4);
//    glVertex2f(-0.6,-0.6);
//    glVertex2f(0.6,-0.6);
//    glVertex2f(-0.6,-0.8);
//    glVertex2f(0.6,-0.8);
//    glEnd();
    while (doRendering) {
        if (doResize) {
            glViewport(0, 0, w, h);
            doResize = false;
        }

        glw->swapBuffers();
        msleep(40);
    }
}
соединение этих классов происходит в еще одном классе- наследнике QMainWindow. в таком формате происходит добавление нескольких новых потоков,которые будут показывать вращение картинки.. если есть возможность помочь исправить и оставить только 2 потока, буду рада. т.е. в одном потоке создается виджет, а во втором он отрисовывается.. это необходимо по рабочим соображениям..
класс AppWindow:
Код:
#ifndef APPWINDOW_H
#define APPWINDOW_H
#include <QMainWindow>
#include <QWorkspace>
class AppWindow: public QMainWindow
{
    Q_OBJECT
public:
    AppWindow();

protected:
    void closeEvent(QCloseEvent *evt);

private slots:
    void newThread();
  //  void killThread();

private:
    QWorkspace *ws;
};

#endif // APPWINDOW_H
реализация:
Код:
#include "appwindow.h"
#include "glwidget.h"
#include "glthread.h"
#include<QtGui>
#include <QMenu>
#include<QMenuBar>
#include <QAction>
AppWindow::AppWindow()
    : QMainWindow(0)
{
    QMenu *menu = new QMenu(this);
    menuBar()->addMenu(menu);
    menu=menuBar()->addMenu("&Thread");

    menu->addAction("&New thread", this, SLOT(newThread()), Qt::CTRL+Qt::Key_N);
   // menu->addAction("&End current thread", this, SLOT(killThread()), Qt::CTRL+Qt::Key_K);
    menu->addSeparator();
    menu->addAction("&Exit", qApp, SLOT(quit()), Qt::CTRL+Qt::Key_Q);

    menuBar()->show();
    ws = new QWorkspace(this);
    setCentralWidget(ws);
}

void AppWindow::closeEvent(QCloseEvent *evt)
{
    QWidgetList windows = ws->windowList();
    for (int i = 0; i < int(windows.count()); ++i) {
        GLWidget *window = (GLWidget *)windows.at(i);
        window->stopRendering();
    }
    QMainWindow::closeEvent(evt);
}

void AppWindow::newThread()
{
    QWidgetList windows = ws->windowList();
    GLWidget *widget = new GLWidget(ws);
    widget->setWindowTitle("Thread #" + QString::number(windows.count() + 1));
   // widget->resize(200,200);
    widget->show();
    widget->startRendering();
}

//void AppWindow::killThread()
//{
//    GLWidget *widget = (GLWidget *)ws->activeWindow();
//    if (widget) {
//    widget->stopRendering();
//        delete widget;
//    }
//}
ну и сам мейн
Код:
#include <QApplication>
#include "appwindow.h"
#include "glwidget.h"
#include "glthread.h"

int main(int argc, char *argv[])
{
        QApplication app(argc, argv);
        AppWindow aw;
       // app.setMainWidget(&aw);
        aw.show();
        return app.exec();
}
реализация в одном потоке работает отлично, потому что там не надо запариваться с контекстами и т.д.


Название: Re: Использование OpenGL во втором потоке на Qt
Отправлено: twp от Июль 08, 2011, 15:35
а можно сам архив проекта приаттачить к теме? Просто создавать весь проект влом  :)


Название: Re: Использование OpenGL во втором потоке на Qt
Отправлено: Tataina от Июль 08, 2011, 16:16
под Qt creator 2.2.1


Название: Re: Использование OpenGL во втором потоке на Qt
Отправлено: twp от Июль 08, 2011, 18:03
ок, попробую на досуге. вообще есть  рабочий пример (http://mih.voxindeserto.de/threadedcube.html) Но там используются мютексы что имхо не есть хорошо, возможно есть возможность обойтись без них


Название: Re: Использование OpenGL во втором потоке на Qt
Отправлено: Igors от Июль 09, 2011, 11:25
Мои познания в OpenGL допотопны, тоже интересно узнать как это делается. По смыслу вроде нет помех вызывать все ф-ции OpenGL из др. нитки (ведь нужно просто "не в главной" а не "параллельно"). А вот SwapBuffers наверняка завалит систему. Я бы спробовал так

- отключить SwapBuffers и убедиться что все живет
- вместо SwapBuffers извлечь пиксели из внутреннего буфера (glReadPixels) и в главной нитке его уже рисовать

Не исключено этот примитивный вариант будет работать


Название: Re: Использование OpenGL во втором потоке на Qt
Отправлено: Tataina от Июль 09, 2011, 16:13
swap действительно это заваливает =) подготовка данных в другом потоке- это очень популярная тема, и на многих форумах это обсуждается, и много где, предлагаются решения, но это не совсем то,что нужно лично для моей задачи. Это поможет улучшить,но все также будет грузить основной поток, который и так нагружен по самое не балуйся..

Пример с немецкого форума еще не проверяла, пыталась решить проблему со своим виджетом, который совсем не хочет отображаться..


Название: Re: Использование OpenGL во втором потоке на Qt
Отправлено: twp от Июль 11, 2011, 15:01
доработал я пример и все работает.
Я просто взял пример из книги по OpenGL и адаптировал его к этому приложению.
То я и спрашивал, работает ли приложение без использования потока.
Это приложение врядли подойдет ибо здесь происходит постоянная перерисовка через каждые 40 мс
Лучше за основу взять приложение с немецкого форума. Там отрисовка происходит только когда это действительно необходимо
и больше подходит для статических сцен.
Насчет SwapBuffers. Он используется в потоке в обоих демках и не валит приложение. Насколько я понял, главное это сделать QGLWidget::makeCurrent() перед операциями отрисовки и SwapBuffers, а после этого - QGLWidget::doneCurrent(). Вообще исходя из этой статьи (http://labs.qt.nokia.com/2011/06/03/threaded-opengl-in-4-8/) , Qt 4.8 и выше будет поддерживать больше возможностей использования потоков в OpenGL