2012-06-05 2 views
2

В настоящее время мы сталкиваемся со следующей проблемой: у нас есть приложение, которое должно отображать множество отдельных сцен OpenSceneGraph в разных виджетах Qt. Например, у нас может быть один виджет Qt, изображающий сферу, а другой виджет изображает икосаэдр. Поскольку мы используем OpenSceneGraph 3.0.1, мы следовали за osgViewerQt example from the official documentation для реализации этого.Несколько виджетов Qt, отображающих разные узлы OpenSceneGraph без потери производительности

Пример кода использует QTimer для того, чтобы заставить обновления для просмотра виджета:

connect(&_timer, SIGNAL(timeout()), this, SLOT(update())); 
    _timer.start(10); 

Проблемы начинаются, когда в настоящее время мы хотим создать и показать несколько виджетов. Поскольку каждый виджет имеет свой собственный таймер, производительность быстро уменьшает с количеством открытых виджетов. Мало того, что взаимодействие с виджетами OSG очень медленное, также взаимодействие с другое Qt-виджеты заметно отстают. Даже половина недавней четырехъядерной системы почти перегружена, когда открыто около 5 окон. Эта проблема определенно не относится к нашему графическому оборудованию. Другие приложения могут отображать гораздо большие сцены (Blender, Meshlab и т. Д.) без любое отрицательное воздействие на производительность.

Итак, подведем итог: что было бы лучшим способом создания нескольких виджетов Qt, показывающих разные сцены OpenSceneGraph без влияние производительности?

То, что мы уже пробовали:

  • Мы уже рассматривали с помощью одного osgViewer::CompositeViewer для рендеринга всех объектов сцены. Однако мы отказались от этой идеи для , потому что она, вероятно, сделает взаимодействие с одним виджетами очень сложным.
  • Мы попытались поместить часть рендеринга каждого osgViewer::CompositeViewer в отдельной теме, как подробно описано osgQtWidgets example.

Наша вторая попытка (с помощью нитей) выглядела примерно так:

class ViewerFrameThread : public OpenThreads::Thread 
    { 
     public: 
      ViewerFrameThread(osgViewer::ViewerBase* viewerBase): 
       _viewerBase(viewerBase) {} 

      ~ViewerFrameThread() 
      { 
       cancel(); 
       while(isRunning()) 
       { 
        OpenThreads::Thread::YieldCurrentThread(); 
       } 
      } 

      int cancel() 
      { 
       _viewerBase->setDone(true); 
       return 0; 
      } 

      void run() 
      { 
       int result = _viewerBase->run(); 
      } 

      osg::ref_ptr<osgViewer::ViewerBase> _viewerBase; 
    }; 

Однако это также привело к заметному снижению производительности. Каждый поток по-прежнему требует большого времени процессора (что неудивительно, так как основное взаимодействие все еще обрабатывается таймером). Единственным преимуществом этого подхода является то, что по крайней мере взаимодействие с другими Qt виджетов остается возможным.

Идеальное решение для нас было бы виджет, который срабатывает только перерисовывать запросы всякий раз, когда пользователь взаимодействует с ним, например, нажав, дважды щелкнув, прокрутки и т.д. Более точно, этот виджет должен оставаться idle, пока не потребуется обновление. Что-то похожее на это возможно на всех? Мы будем приветствовать любые предложения.

ответ

1

Попробовав несколько моделей для этой проблемы, я рад сообщить, что нашел тот, который работает отлично. Я использую QThread (аналогично описанной выше теме), которая по существу обертывает объект osgViewer::ViewerBase и просто вызывает viewer->run().

Трюк, позволяющий снизить нагрузку на процессор, заключается в том, чтобы заставить OpenSceneGraph визуализировать по запросу. После опробовали различные варианты, я нашел следующие две настроек для работы лучше всего:

viewer->setRunFrameScheme(osgViewer::ViewerBase::ON_DEMAND); 
viewer->setThreadingModel(osgViewer::ViewerBase::CullDrawThreadPerContext); 

Зрителя, который модифицирован, как это будет не использование паразитных циклов CPU для непрерывного обновления в то же время с использованием нескольких потоков для выбраковки и Рисунок. В некоторых случаях другие модели резьбы могут, конечно, работать лучше, но для меня этого было достаточно.

Если кто-либо попытается выполнить аналогичное решение, будьте предупреждены о том, что некоторые операции требуют явных запросов на перерисовку. Например, при обработке взаимодействий с объектами OSG или когда вы пишете свой собственный класс CameraManipulator, это не помешает позвонить viewer->requestRedraw() после изменения настроек просмотра. В противном случае зритель будет обновляться только тогда, когда виджет требует перерисовки.

Короче, вот что я узнал:

  • Не использовать таймеры для оказания
  • Не сдавайтесь на несколько потоков только пока
  • Ничто не сравнится чтение исходного кода (официальный Примеры OSG иногда были недостаточными по деталям, но источник никогда не был ...)
0

Должно быть возможно.Я не могу поверить, что они использовали таймер, работающий на частоте 100 Гц, для обновления виджетов - это действительно не правильный способ сделать это.

Я не знаю об архитектуре OSG, но вам нужно понять способ получения обратного вызова из OSG при изменении данных. В обратном вызове, просто очередь события обновления для соответствующего виджета следующим образом:

void OSGCallbackForWidgetA() 
{ 
    QCoreApplication::postEvent(widgetA, new QEvent(QEvent::UpdateRequest), 
           Qt::LowEventPriority); 
} 

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

0

Я столкнулся с подобной проблемой, когда у меня есть несколько зрителей OSG в одном приложении Qt, где один из них работает хорошо, а t он отдыхает очень «медленно».

Включив статистику рендеринга (нажмите «s» на экране просмотра), я обнаружил, что «медленный» на самом деле не вызван рендерингом, на самом деле рендеринг выполняется быстро.

Причина, по которой зрители являются «медленными», заключается в том, что многие события gui не обрабатываются. Например, когда вы перетаскиваете сцену, вы перетаскиваете сцену, многие события перетаскивания генерируются Qt, но лишь немногие передаются в просмотрщик OSG, поэтому реакция зрителей «медленно».

Откат событий на самом деле связан с тем, что рендеринг слишком велик ... в Viewer :: eventTraversal() обрабатываются только те относительные новые события, а «относительное новое» измеряется cutOffTime = _frameStamp-> getReferenceTime().Поэтому, если Qt генерирует события медленнее, чем рендеринг, многие события будут обрезаны и, следовательно, не обработаны.

И, наконец, после того, как я нашел основную причину, решение легко. Давайте обмануть немного на опорное время _frameStamp, используемом в средстве просмотра :: eventTraversal():

class MyViewer : public osgViewer::Viewer 
{ 
    ... 
    virtual void eventTraversal(); 
    ... 
} 
void MyViewer::eventTraversal() 
{ 
    double reference_time = _frameStamp->getReferenceTime(); 
    _frameStamp->setReferenceTime(reference_time*100); 

    osgViewer::Viewer::eventTraversal(); 

    _frameStamp->setReferenceTime(reference_time); 

    return; 
} 
0

я провел также некоторое время, чтобы понять, как сделать эту работу.

Я думаю, что ответ от Gnosophilon не может работать с Qt5, так как правила переключения контекстного потока более строгие, чем с Qt4 (т. Е. Для объекта контекста OpenGL требуется вызов moveToThread()). На момент написания этой статьи OSG не удовлетворяет этим правилам. (По крайней мере, я не смог заставить это работать)

Я не понял, как это сделать в отдельном потоке, однако, чтобы сделать сцену плавно в потоке пользовательского интерфейса без использования таймера с фиксированным интервалом, один может сделать следующее

viewer_->setRunFrameScheme(osgViewer::ViewerBase::ON_DEMAND); 
viewer_->setThreadingModel(osgViewer::CompositeViewer::SingleThreaded); 
osgQt::initQtWindowingSystem(); 
osgQt::setViewer(viewer_.get()); 

osgQt::setViewer() ручку с глобальными переменными, поэтому только один зритель в то время, может быть использован. (который может быть CompositeViewer, конечно)

Смежные вопросы