Как установить позицию QQuickItem в точном кадре из С++?

Мое мобильное приложение использует парадигму OpenGL под QML.

Ориентация камеры моей сцены GL синхронизируется с датчиком движения (магнитометр и акселерометр) мобильного устройства. Я также показываю 2D-метку поверх этой 3D-сцены, положение которой на экране должно соответствовать заданной 3D-позиции в моей GL-сцене. Эти метки являются обычными элементами QML, которыми я управляю со стороны C++ своего приложения.

Для этого я делаю прямое подключение к сигналу QQuickWindow::beforeSynchronising(), где ориентация камеры синхронизируется с датчиком движения. Затем я проецирую 3D-точку на экран, чтобы найти положение метки, и, наконец, вызываю QQuickItem::setPosition().

Сцена GL рендерится как обычно, используя прямое подключение к сигналу QQuickWindow::beforeRendering().

MyAppController::MyAppController() : QObject() {
    connect(window, SIGNAL(beforeSynchronising()), this, SLOT(sync()), Qt::DirectConnection);
    connect(window, SIGNAL(beforeRendering()), this, SLOT(render()), Qt::DirectConnection);

    m_camera = ...; // create camera
    m_scene = ...;  // create GL scene
    m_quickItem = ...; // load label QML item

    // start updating the window at a constant frame rate
    QTimer* timer = new QTimer(this);
    connect (timer, SIGNAL(timeout()), this, SLOT(update()));
    timer->start(1000.0 / 60.0);   // 60 FPS
}

MyAppController::sync() {
    m_camera->syncWithMotionSensor();

    // use camera matrix to calculate the projection of a 3D point on the screen
    QPointF pos = ...;  
    m_quickItem->setPosition(pos);  // set the label position
}

MyAppController::render() {
    m_scene->draw(); // OpenGL code
}

Все хорошо, за исключением того, что мои метки кажутся отстающими на один кадр по сравнению с моей GL-сценой, и я думаю, что понимаю, почему: вызов QQuickItem::setPosition() из потока QSGRendering, по-видимому, слишком поздно для QSGNode для обновления для этого кадра. Для этого элемента запланировано обновление, но оно не вступит в силу до следующего кадра. Отсюда и задержка в 1 кадр.

Вы можете подумать, что это не так уж и плохо, однако это создает странный эффект и не помогает прочитать, что на этикетке, потому что глаза также следят за фоном (помните, что камера синхронизируется с датчиком движения устройства).

Я пытался использовать сигнал QQuickWindow::afterAnimating(), но задержка еще больше. Это нормально, так как этот сигнал исходит из потока графического интерфейса, и, таким образом, у нас еще меньше контроля над тем, в каком кадре будет синхронизироваться QSGRenderThread.

Итак, мой вопрос: как я могу добиться позиционирования элемента QML из С++ в точном кадре? Возможно ли это даже с использованием QML? Было бы лучше, если бы мои метки были чистыми C++ QQuickItems, и если бы вместо вызова setPosition() я использовал свою собственную функцию, которая гарантирует, что QSGNode получит обновление?


person Konstantinos Gaitanis    schedule 05.06.2015    source источник


Ответы (1)


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

MyAppController::sync() {
    m_scene->prepare();  // uses the camera matrix from previous frame    
    m_camera->syncWithMotionSensor();  // update camera matrix
    // use camera matrix to calculate the projection of a 3D point on the screen
    QPointF pos = ...;  
    m_quickItem->setPosition(pos);  // set the label position, will be updated on the next frame
}

MyAppController::render() {
    m_scene->draw(); // execute OpenGL code
}
person Konstantinos Gaitanis    schedule 06.06.2015