Как я могу оптимизировать производительность приложения на основе QGraphicsView? - PullRequest
5 голосов
/ 11 октября 2011

У меня есть приложение, основанное на платформе Qt Graphics View.
Это игра-головоломка, которая в основном разрезает растровое изображение на более мелкие растровые изображения (кусочки головоломки) и отображает их как QGraphicsItem s за QGraphicsView. Я хочу, чтобы это приложение работало на смартфонах и планшетах. (он уже работает на Nokia N900 и некоторых телефонах Symbian. Пока не оптимизирован для Symbian ^ 3.)
Источник: на Gitorious .

Элементы наследуют QGraphicsItem и QObject и имеют макросы Q_PROPERTY для pos() и rotation() QGraphicsItem, позволяющие анимировать их с помощью инфраструктуры Qt Animation.
Я выполняю преобразования элементов, такие как масштабирование и вращение (последнее только в разрабатываемой ветке мультитач), и я также использую QGraphicsDropShadowEffect для них.

Я использую QGLWidget в качестве области просмотра QGraphicsView, чтобы включить ускорение OpenGL для приложения.

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

В чем секрет? Как я могу оптимизировать это приложение?

Ответы [ 5 ]

5 голосов
/ 23 января 2012

Хорошо, я так долго ждал решения.

Тем временем я переписал пользовательский интерфейс приложения в QML, и, к моему удивлению, производительность намного выше, а приложение теперь очень плавное.

Некоторые выводы:

  • Ускорение OpenGL лучше всего при работе в полноэкранном режиме. Наличие всего пользовательского интерфейса в QDeclarativeView и установка его viewPort для QGLWidget и отображение его в полноэкранном режиме делают это возможным.
  • Кажется, что издержки QWidgets намного больше, чем мы думали.
  • QML может работать намного лучше, чем ожидалось.
  • Воздействие QGraphicsDropshadowEffect было незначительным, но я удалил его и теперь вместо этого использую эффект обводки. В будущем я мог бы рассмотреть возможность использования шейдерных эффектов QML.
  • Стоит установить все виды флагов оптимизации для QDeclarativeView
  • Рисование элементов с альфа-прозрачностью выполняется намного хуже, чем без них. По возможности избегайте альфа-прозрачности.
  • Переписать подкласс QGraphicsItem в подкласс QDeclarativeItem действительно просто и стоит усилий.

Исходный код здесь.

3 голосов
/ 02 марта 2013

Мой ответ для людей, которые, как я некоторое время назад, реализуют логику режима рендеринга в их методе GraphicsItem::paint(). Например:

GraphicsItem::paint(QPainter * painter, const QStyleOptionGraphicsItem*, QWidget*)
{
    QPen _pen ;
    const qreal normalPenWidthF = 1.5 ;
    if(isSelected()) {
        _pen.setColor(Settings::s_selectionColor) ;
        _pen.setWidthF(Settings::s_selectionWidth) ;
    }
    else
    if(isHovered()) {
        _pen.setColor(Settings::s_hoveredColor) ;
        _pen.setWidthF(Settings::s_selectionWidth) ;
    }
    else
    if(someOtherLogic()) {
        _pen.setColor(Settings::s_otherColor) ;
        _pen.setWidthF(normalPenWidthF) ;
    }
    else {
        _pen.setColor(TSPSettings::s_defaultColor) ;
        _pen.setWidthF(normalPenWidthF) ;
    }
    //
    painter->setPen(_pen) ;
    painter->setBrush(Qt::NoBrush) ;
    painter->drawEllipse(m_rect) ;
}

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

  1. Пользовательские графические элементы должны наследовать QAbstractGraphicsShapeItem, поэтому у вас есть поддержка setPen () и setBrush ().
  2. Предоставьте интерфейс для обновления пера и кисти и используйте некоторую логику для запускать обновление только при необходимости.

.h

class AbstractGraphicsItem : public QAbstractGraphicsShapeItem
{
    private :
        bool m_hovered ;

    public :
        AbstractGraphicsItem() ;
        virtual ~AbstractGraphicsItem() ;

        bool isHovered() const { return m_hovered ; }
        void setIsHovered(bool state) ;

        // control render mode update
        virtual void updatePenAndBrush()=0 ;

    protected :
        virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value) ;
        virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *e);
        virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *e);
};

.cpp

AbstractGraphicsItem::AbstractGraphicsItem()
    : QAbstractGraphicsShapeItem()
    , m_hovered(false)
{
}

AbstractGraphicsItem::~AbstractGraphicsItem()
{
}

void AbstractGraphicsItem::setHovered(bool state)
{
    if (h!=isHovered()) {
        m_hovered = h ;
        updatePenAndBrush() ;
        update() ;
    }
}

void AbstractGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent*)
{
    setHovered(true) ;
}

void AbstractGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent*)
{
    setHovered(false) ;
}

QVariant AbstractGraphicsItem::itemChange(GraphicsItemChange change, const QVariant &value)
{
    switch(change) {
        case ItemSelectedHasChanged :
            updatePenAndBrush() ;
            break ;
    }

    return QAbstractGraphicsShapeItem::itemChange(change, value);
}

И тогда ваш GraphicsItem (который наследует AbstractGraphicsItem) становится:

void GraphicsItem::updatePenAndBrush()
{
    QPen _pen ;
    if(isSelected()) {
        _pen.setColor(Settings::s_selectionColor) ;
        _pen.setWidthF(Settings::s_selectionWidth) ;
    } else
    if(isHovered()) {
        _pen.setColor(Settings::s_hoveredColor) ;
        _pen.setWidthF(Settings::s_selectionWidth) ;
    } else
    if(someOtherLogic()) {
        _pen.setColor(Settings::s_otherColor) ;
        _pen.setWidthF(normalPenWidthF) ;
    } else {
        _pen.setColor(Settings::s_defaultColor) ;
        _pen.setWidthF(normalPenWidthF) ;
    }
    _pen.setCosmetic(true) ;
    setPen(_pen) ;
}

void GraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget *)
{
    painter->setPen(pen()) ;
    painter->setBrush(brush()) ;
    painter->drawEllipse(s_rect) ;
}

Содержимое старого метода GraphicsItem::paint() теперь находится в GraphicsItem::updatePenAndBrush() и вызывается время от времени, но не при каждом вызове рисования. С другой стороны, метод рисования доходит до основ. Очевидно, вам придется позвонить updatePenAndBrush() самостоятельно, но в моем случае это было несложно. Это не единственное, что я сделал, чтобы улучшить производительность. Я много искал, и есть много возможностей для настройки системы Graphics View, но с этим мое приложение перешло от едва пригодного к использованию в реальном времени (наконец-то!)

2 голосов
/ 11 октября 2011

Обычно лучше всего установить Graphicssystem на «растр» (конечный результат все равно будет OpenGL из-за GL Widget в качестве области просмотра). Вы не упоминаете об этом, но вы можете легко попробовать, если добавление «-graphicssystem raster» в командную строку приводит к каким-либо улучшениям.

2 голосов
/ 11 октября 2011

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

2 голосов
/ 11 октября 2011

Особенно, когда у вас есть движущиеся элементы в сцене, индексации QGraphicsScene может потребоваться некоторое время для обновления индекса, что снижает производительность. Вы можете настроить индексирование с помощью setItemIndexMethod () . Если вы не полагаетесь на items () или itemAt () , это может помочь повысить производительность.

Однако это длинный выстрел. Если на вашей сцене только несколько предметов, улучшения производительности могут быть минимальными.

...