summaryrefslogtreecommitdiffstats
path: root/src/waveform/vsyncthread.cpp
blob: 2105d9221a2f21a8f9d750a9a4d93ce5e2f164f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include "vsyncthread.h"

#include <QGLFormat>
#include <QThread>
#include <QTime>
#include <QtDebug>

#include "moc_vsyncthread.cpp"
#include "util/math.h"
#include "util/performancetimer.h"
#include "waveform/guitick.h"

VSyncThread::VSyncThread(QObject* pParent)
        : QThread(pParent),
          m_bDoRendering(true),
          m_vSyncTypeChanged(false),
          m_syncIntervalTimeMicros(33333),  // 30 FPS
          m_waitToSwapMicros(0),
          m_vSyncMode(ST_TIMER),
          m_syncOk(false),
          m_droppedFrames(0),
          m_swapWait(0),
          m_displayFrameRate(60.0),
          m_vSyncPerRendering(1) {
}

VSyncThread::~VSyncThread() {
    m_bDoRendering = false;
    m_semaVsyncSlot.release(2); // Two slots
    wait();
    //delete m_glw;
}

void VSyncThread::run() {
    QThread::currentThread()->setObjectName("VSyncThread");

    m_waitToSwapMicros = m_syncIntervalTimeMicros;
    m_timer.start();

    //qDebug() << "VSyncThread::run()";
    while (m_bDoRendering) {
        if (m_vSyncMode == ST_FREE) {
            // for benchmark only!

            // renders the waveform, Possible delayed due to anti tearing
            emit vsyncRender();
            m_semaVsyncSlot.acquire();

            emit vsyncSwap(); // swaps the new waveform to front
            m_semaVsyncSlot.acquire();

            m_timer.restart();
            m_waitToSwapMicros = 1000;
            usleep(1000);
        } else { // if (m_vSyncMode == ST_TIMER) {
            emit vsyncRender(); // renders the new waveform.

            // wait until rendering was scheduled. It might be delayed due a
            // pending swap (depends one driver vSync settings)
            m_semaVsyncSlot.acquire();

            // qDebug() << "ST_TIMER                      " << lastMicros << restMicros;
            int remainingForSwap = m_waitToSwapMicros - static_cast<int>(
                m_timer.elapsed().toIntegerMicros());
            // waiting for interval by sleep
            if (remainingForSwap > 100) {
                usleep(remainingForSwap);
            }

            // swaps the new waveform to front in case of gl-wf
            emit vsyncSwap();

            // wait until swap occurred. It might be delayed due to driver vSync
            // settings.
            m_semaVsyncSlot.acquire();

            // <- Assume we are VSynced here ->
            int lastSwapTime = static_cast<int>(m_timer.restart().toIntegerMicros());
            if (remainingForSwap < 0) {
                // Our swapping call was already delayed
                // The real swap might happens on the following VSync, depending on driver settings
                m_droppedFrames++; // Count as Real Time Error
            }
            // try to stay in right intervals
            m_waitToSwapMicros = m_syncIntervalTimeMicros +
                    ((m_waitToSwapMicros - lastSwapTime) % m_syncIntervalTimeMicros);
        }
    }
}

int VSyncThread::elapsed() {
    return static_cast<int>(m_timer.elapsed().toIntegerMicros());
}

void VSyncThread::setSyncIntervalTimeMicros(int syncTime) {
    m_syncIntervalTimeMicros = syncTime;
    m_vSyncPerRendering = static_cast<int>(
            round(m_displayFrameRate * m_syncIntervalTimeMicros / 1000));
}

void VSyncThread::setVSyncType(int type) {
    if (type >= (int)VSyncThread::ST_COUNT) {
        type = VSyncThread::ST_TIMER;
    }
    m_vSyncMode = (enum VSyncMode)type;
    m_droppedFrames = 0;
    m_vSyncTypeChanged = true;
}

int VSyncThread::toNextSyncMicros() {
    int rest = m_waitToSwapMicros - static_cast<int>(m_timer.elapsed().toIntegerMicros());
    // int math is fine here, because we do not expect times > 4.2 s
    if (rest < 0) {
        rest %= m_syncIntervalTimeMicros;
        rest += m_syncIntervalTimeMicros;
    }
    return rest;
}

int VSyncThread::fromTimerToNextSyncMicros(const PerformanceTimer& timer) {
    int difference = static_cast<int>(m_timer.difference(timer).toIntegerMicros());
    // int math is fine here, because we do not expect times > 4.2 s
    return difference + m_waitToSwapMicros;
}

int VSyncThread::droppedFrames() {
    return m_droppedFrames;
}

void VSyncThread::vsyncSlotFinished() {
    m_semaVsyncSlot.release();
}

void VSyncThread::getAvailableVSyncTypes(QList<QPair<int, QString > >* pList) {
    for (int i = (int)VSyncThread::ST_TIMER; i < (int)VSyncThread::ST_COUNT; i++) {
        //if (isAvailable(type))  // TODO
        {
            enum VSyncMode mode = (enum VSyncMode)i;

            QString name;
            switch (mode) {
            case VSyncThread::ST_TIMER:
                name = tr("Timer (Fallback)");
                break;
            case VSyncThread::ST_MESA_VBLANK_MODE_1:
                name = tr("MESA vblank_mode = 1");
                break;
            case VSyncThread::ST_SGI_VIDEO_SYNC:
                name = tr("Wait for Video sync");
                break;
            case VSyncThread::ST_OML_SYNC_CONTROL:
                name = tr("Sync Control");
                break;
            case VSyncThread::ST_FREE:
                name = tr("Free + 1 ms (for benchmark only)");
                break;
            default:
                break;
            }
            QPair<int, QString > pair = QPair<int, QString >(i, name);
            pList->append(pair);
        }
    }
}