Skip to content

Commit

Permalink
OpenGL: Add MythOpenGLPerf - an OpenGL timer
Browse files Browse the repository at this point in the history
- a simple sub-class of QOpenGLTimeMonitor to record the exection time
of blocks of OpenGL code
- add OpenGL performance monitoring for VideoOutputOpenGL and run when -
v gpu verbosity is enabled (i.e. mythfrontend -v gpu)
- execution time is recorded for texture upload (when applicable),
framebuffer clearing, rendering, flushing and the final framebuffer swap.
  • Loading branch information
mark-kendall committed Sep 13, 2019
1 parent c1bffb8 commit 733acd1
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 2 deletions.
57 changes: 56 additions & 1 deletion mythtv/libs/libmythtv/videoout_opengl.cpp
Expand Up @@ -6,6 +6,7 @@
#include "videodisplayprofile.h"
#include "osd.h"
#include "mythuihelper.h"
#include "mythopenglperf.h"
#include "mythrender_opengl.h"
#include "mythpainter_ogl.h"
#include "mythcodeccontext.h"
Expand Down Expand Up @@ -113,7 +114,8 @@ VideoOutputOpenGL::VideoOutputOpenGL(const QString &Profile)
m_newVideoDim(),
m_newVideoDispDim(),
m_newAspect(0.0f),
m_buffersCreated(false)
m_buffersCreated(false),
m_openGLPerf(nullptr)
{
// Setup display switching
if (gCoreContext->GetBoolSetting("UseVideoModes", false))
Expand All @@ -131,6 +133,13 @@ VideoOutputOpenGL::VideoOutputOpenGL(const QString &Profile)
m_render->IncrRef();
OpenGLLocker locker(m_render);

// enable performance monitoring if requested
// will report the execution time for the key GL code blocks
// N.B. 'Upload' should always be zero when using hardware decoding and direct
// rendering. Any copy cost for direct rendering will be included within 'Render'
if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
m_openGLPerf = new MythOpenGLPerf("GLVidPerf: ", { "Upload:", "Clear:", "Render:", "Flush:", "Swap:" });

// Disallow unsupported video texturing on GLES2
if (m_render->isOpenGLES() && m_render->format().majorVersion() < 3)
{
Expand Down Expand Up @@ -175,7 +184,12 @@ VideoOutputOpenGL::~VideoOutputOpenGL()
if (m_openGLVideo)
delete m_openGLVideo;
if (m_render)
{
m_render->makeCurrent();
delete m_openGLPerf;
m_render->doneCurrent();
m_render->DecrRef();
}
m_render = nullptr;
}

Expand Down Expand Up @@ -380,6 +394,10 @@ void VideoOutputOpenGL::ProcessFrame(VideoFrame *Frame, OSD */*osd*/,

OpenGLLocker ctx_lock(m_render);

// start the first timer
if (m_openGLPerf)
m_openGLPerf->RecordSample();

// Process input changes
if (m_newCodecId != kCodec_NONE)
{
Expand Down Expand Up @@ -428,6 +446,10 @@ void VideoOutputOpenGL::ProcessFrame(VideoFrame *Frame, OSD */*osd*/,
if (m_openGLVideo && swframe && !dummy)
m_openGLVideo->ProcessFrame(Frame, Scan);

// time texture update
if (m_openGLPerf)
m_openGLPerf->RecordSample();

if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
m_render->logDebugMarker(LOC + "PROCESS_FRAME_END");
}
Expand Down Expand Up @@ -461,13 +483,28 @@ void VideoOutputOpenGL::PrepareFrame(VideoFrame *Frame, FrameScanType Scan, OSD
Frame = m_videoBuffers.Tail(kVideoBuffer_pause);
}

// if process frame has not been called (double rate hardware deint), then
// we need to start the first 2 performance timers here
if (m_openGLPerf)
{
if (!m_openGLPerf->GetTimersRunning())
{
m_openGLPerf->RecordSample();
m_openGLPerf->RecordSample();
}
}

m_render->BindFramebuffer(nullptr);
if (m_dbLetterboxColour == kLetterBoxColour_Gray25)
m_render->SetBackground(127, 127, 127, 255);
else
m_render->SetBackground(0, 0, 0, 255);
m_render->ClearFramebuffer();

// time framebuffer clearing
if (m_openGLPerf)
m_openGLPerf->RecordSample();

// stereoscopic views
QRect main = m_render->GetViewPort();
QRect first = main;
Expand Down Expand Up @@ -559,8 +596,16 @@ void VideoOutputOpenGL::PrepareFrame(VideoFrame *Frame, FrameScanType Scan, OSD
}
}

// time rendering
if (m_openGLPerf)
m_openGLPerf->RecordSample();

m_render->Flush();

// time flush
if (m_openGLPerf)
m_openGLPerf->RecordSample();

if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
m_render->logDebugMarker(LOC + "PREPARE_FRAME_END");
}
Expand Down Expand Up @@ -627,6 +672,16 @@ void VideoOutputOpenGL::Show(FrameScanType /*scan*/)
if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
m_render->logDebugMarker(LOC + "SHOW");
m_render->swapBuffers();
if (m_openGLPerf)
{
// time buffer swap and log
// Results will normally be available on the next pass - and we will typically
// test every other frame as a result to avoid blocking in the driver.
// With the default of averaging over 30 samples this should give a 30 sample
// average over 60 frames
m_openGLPerf->RecordSample();
m_openGLPerf->LogSamples();
}
m_render->doneCurrent();
}
}
Expand Down
6 changes: 5 additions & 1 deletion mythtv/libs/libmythtv/videoout_opengl.h
@@ -1,12 +1,13 @@
#ifndef VIDEOOUT_OPENGL_H_
#define VIDEOOUT_OPENGL_H_

// MythTV headers
// MythTV
#include "videooutbase.h"
#include "openglvideo.h"

class MythRenderOpenGL;
class MythOpenGLPainter;
class MythOpenGLPerf;

class VideoOutputOpenGL : public VideoOutput
{
Expand Down Expand Up @@ -63,6 +64,9 @@ class VideoOutputOpenGL : public VideoOutput
QSize m_newVideoDispDim;
float m_newAspect;
bool m_buffersCreated;

// performance monitoring (-v gpu)
MythOpenGLPerf *m_openGLPerf;
};

#endif
3 changes: 3 additions & 0 deletions mythtv/libs/libmythui/libmythui.pro
Expand Up @@ -178,6 +178,9 @@ using_opengl {
SOURCES += mythpainter_ogl.cpp mythrender_opengl.cpp
HEADERS += mythpainter_ogl.h mythrender_opengl.h
HEADERS += mythrender_opengl_defs.h mythrender_opengl_shaders.h
HEADERS += mythopenglperf.h
SOURCES += mythopenglperf.cpp

using_opengles {
DEFINES += USING_OPENGLES
}
Expand Down
89 changes: 89 additions & 0 deletions mythtv/libs/libmythui/mythopenglperf.cpp
@@ -0,0 +1,89 @@
// MythTV
#include "mythlogging.h"
#include "mythopenglperf.h"

/*! \class MythOpenGLPerf
* \brief A simple overload of QOpenGLTimeMonitor to record and log OpenGL execution intervals
*/
MythOpenGLPerf::MythOpenGLPerf(const QString &Name,
QVector<QString> Names,
int SampleCount)
: m_name(Name),
m_totalSamples(SampleCount),
m_timerNames(Names)
{
while (m_timerData.size() < m_timerNames.size())
m_timerData.append(0);

setSampleCount(static_cast<int>(m_timerNames.size()) + 1);
if (!create())
{
LOG(VB_GENERAL, LOG_WARNING, m_name + "Failed to initialise OpenGL timers");
m_timersReady = false;
return;
}

LOG(VB_GENERAL, LOG_INFO, m_name + QString("Created %1 GL timers- averaging over %2 samples")
.arg(sampleCount()).arg(m_totalSamples));
}

int MythOpenGLPerf::GetTimersRunning(void)
{
return m_timersRunning;
}

void MythOpenGLPerf::RecordSample(void)
{
if (m_timersReady && isCreated())
{
m_timersRunning++;
recordSample();
}
}

void MythOpenGLPerf::LogSamples(void)
{
if (!(isCreated() && isResultAvailable()))
{
m_timersReady = false;
return;
}

// retrieve the samples
QVector<GLuint64> samples = waitForIntervals();

// ensure we have the correct number
if (samples.size() != m_timerData.size())
{
LOG(VB_GENERAL, LOG_ERR, m_name + "Incorrect sample count %1 %2 %3");
reset();
m_timersRunning = 0;
return;
}

// increment our running totals
for (int i = 0; i < samples.size(); ++i)
m_timerData[i] += samples[i];
m_sampleCount++;

// log and reset when necessary
if (m_sampleCount > m_totalSamples)
{
m_sampleCount = 0;
QStringList results;
GLuint64 total = 0;
for (int i = 0; i < m_timerData.size(); ++i)
{
results.append(m_timerNames[i] + QString::number((m_timerData[i] / 1000000000.0) / m_totalSamples, '0', 4));
total += m_timerData[i];
m_timerData[i] = 0;
}
LOG(VB_GPU, LOG_INFO, m_name + results.join(" ") +
QString(" Total fps: %1").arg(1000000000.0 / (static_cast<double>(total) / m_totalSamples)));
}

// clear timers
m_timersReady = true;
m_timersRunning = 0;
reset();
}
29 changes: 29 additions & 0 deletions mythtv/libs/libmythui/mythopenglperf.h
@@ -0,0 +1,29 @@
#ifndef MYTHOPENGLPERF_H
#define MYTHOPENGLPERF_H

// Qt
#include <QVector>
#include <QOpenGLTimeMonitor>

// MythTV
#include "mythuiexp.h"

class MUI_PUBLIC MythOpenGLPerf : public QOpenGLTimeMonitor
{
public:
MythOpenGLPerf(const QString &Name, QVector<QString> Names, int SampleCount = 30);
void RecordSample (void);
void LogSamples (void);
int GetTimersRunning(void);

private:
QString m_name { };
int m_sampleCount { 0 };
int m_totalSamples { 30 };
bool m_timersReady { true };
int m_timersRunning { 0 };
QVector<GLuint64> m_timerData { 0 };
QVector<QString> m_timerNames { };
};

#endif // MYTHOPENGLPERF_H

0 comments on commit 733acd1

Please sign in to comment.