Skip to content

Commit

Permalink
OpenGLVideo: Better reference frame hanlding
Browse files Browse the repository at this point in the history
- explicitly create textures for previous and next frames
- load data into next first and use a much simpler rotation
- if any current frames are not valid, use those that are
- still some minor discontinuity at startup which may be cleaned up
later
  • Loading branch information
mark-kendall committed Mar 14, 2019
1 parent 317bf59 commit d03640a
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 57 deletions.
117 changes: 67 additions & 50 deletions mythtv/libs/libmythtv/openglvideo.cpp
Expand Up @@ -35,12 +35,12 @@ OpenGLVideo::OpenGLVideo(MythRenderOpenGL *Render, VideoColourSpace *ColourSpace
m_hardwareDeinterlacing(false),
m_videoColourSpace(ColourSpace),
m_viewportControl(ViewportControl),
m_referenceTextures(),
m_inputTextures(),
m_prevTextures(),
m_nextTextures(),
m_frameBuffer(nullptr),
m_frameBufferTexture(nullptr),
m_inputTextureSize(m_videoDim),
m_referenceTexturesNeeded(0),
m_features(),
m_extraFeatures(0),
m_resizing(false),
Expand Down Expand Up @@ -115,7 +115,8 @@ OpenGLVideo::~OpenGLVideo()
if (m_shaders[i])
m_render->DeleteShaderProgram(m_shaders[i]);
MythVideoTexture::DeleteTextures(m_render, m_inputTextures);
MythVideoTexture::DeleteTextures(m_render, m_referenceTextures);
MythVideoTexture::DeleteTextures(m_render, m_prevTextures);
MythVideoTexture::DeleteTextures(m_render, m_nextTextures);

m_render->doneCurrent();
m_render->DecrRef();
Expand Down Expand Up @@ -207,15 +208,13 @@ bool OpenGLVideo::AddDeinterlacer(QString &Deinterlacer)
return true;

// delete old reference textures
MythVideoTexture::DeleteTextures(m_render, m_referenceTextures);
MythVideoTexture::DeleteTextures(m_render, m_prevTextures);
MythVideoTexture::DeleteTextures(m_render, m_nextTextures);

// sanity check max texture units. Should only be an issue on old hardware (e.g. Pi)
int max = m_render->GetMaxTextureUnits();
uint texturesperframe = planes(m_outputType);
uint refstocreate = kernel ? 2 : 0;
m_referenceTexturesNeeded = refstocreate + 1;

int totaltextures = texturesperframe * static_cast<int>(m_referenceTexturesNeeded);
int totaltextures = planes(m_outputType) * static_cast<int>(refstocreate + 1);
if (totaltextures > max)
{
LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Insufficent texture units for '%1' (%2 < %3)")
Expand All @@ -229,17 +228,12 @@ bool OpenGLVideo::AddDeinterlacer(QString &Deinterlacer)
return false;

// create the correct number of reference textures
refstocreate *= static_cast<uint>(texturesperframe);
vector<QSize> sizes;
sizes.push_back(QSize(m_videoDim));
while (m_referenceTextures.size() < refstocreate)
if (refstocreate)
{
vector<MythVideoTexture*> textures = MythVideoTexture::CreateTextures(m_render,
m_inputType, m_outputType, sizes);
if (textures.size() != texturesperframe)
return false;
for (uint i = 0; i < textures.size(); ++i)
m_referenceTextures.push_back(textures[i]);
vector<QSize> sizes;
sizes.push_back(QSize(m_videoDim));
m_prevTextures = MythVideoTexture::CreateTextures(m_render, m_inputType, m_outputType, sizes);
m_nextTextures = MythVideoTexture::CreateTextures(m_render, m_inputType, m_outputType, sizes);
}

// ensure they work correctly
Expand Down Expand Up @@ -419,10 +413,23 @@ void OpenGLVideo::UpdateInputFrame(const VideoFrame *Frame)
if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
m_render->logDebugMarker(LOC + "UPDATE_FRAME_START");

if (m_hardwareDeinterlacing)
RotateTextures();
m_videoColourSpace->UpdateColourSpace(Frame);
MythVideoTexture::UpdateTextures(m_render, Frame, m_inputTextures);

// Rotate textures if necessary
bool current = true;
if (m_hardwareDeinterlacing)
{
if (!m_nextTextures.empty() && !m_prevTextures.empty())
{
vector<MythVideoTexture*> temp = m_prevTextures;
m_prevTextures = m_inputTextures;
m_inputTextures = m_nextTextures;
m_nextTextures = temp;
current = false;
}
}

MythVideoTexture::UpdateTextures(m_render, Frame, current ? m_inputTextures : m_nextTextures);

if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
m_render->logDebugMarker(LOC + "UPDATE_FRAME_END");
Expand Down Expand Up @@ -491,7 +498,7 @@ void OpenGLVideo::PrepareFrame(VideoFrame *Frame, bool TopFieldFirst, FrameScanT
if (!format_is_yuv(m_outputType))
program = Default;

if (m_hardwareDeinterlacing && !m_referenceTexturesNeeded)
if (m_hardwareDeinterlacing)
{
if (Scan == kScan_Interlaced)
{
Expand Down Expand Up @@ -541,14 +548,16 @@ void OpenGLVideo::PrepareFrame(VideoFrame *Frame, bool TopFieldFirst, FrameScanT
m_frameBuffer = nullptr;
}
MythVideoTexture::SetTextureFilters(m_render, m_inputTextures, QOpenGLTexture::Linear, QOpenGLTexture::ClampToEdge);
MythVideoTexture::SetTextureFilters(m_render, m_referenceTextures, QOpenGLTexture::Linear, QOpenGLTexture::ClampToEdge);
MythVideoTexture::SetTextureFilters(m_render, m_prevTextures, QOpenGLTexture::Linear, QOpenGLTexture::ClampToEdge);
MythVideoTexture::SetTextureFilters(m_render, m_nextTextures, QOpenGLTexture::Linear, QOpenGLTexture::ClampToEdge);
m_resizing = false;
LOG(VB_PLAYBACK, LOG_INFO, LOC + "Disabled resizing");
}
else if (!m_resizing && resize)
{
MythVideoTexture::SetTextureFilters(m_render, m_inputTextures, QOpenGLTexture::Nearest, QOpenGLTexture::ClampToEdge);
MythVideoTexture::SetTextureFilters(m_render, m_referenceTextures, QOpenGLTexture::Nearest, QOpenGLTexture::ClampToEdge);
MythVideoTexture::SetTextureFilters(m_render, m_prevTextures, QOpenGLTexture::Nearest, QOpenGLTexture::ClampToEdge);
MythVideoTexture::SetTextureFilters(m_render, m_nextTextures, QOpenGLTexture::Nearest, QOpenGLTexture::ClampToEdge);
m_resizing = true;
LOG(VB_PLAYBACK, LOG_INFO, LOC + "Enabled resizing");
}
Expand Down Expand Up @@ -589,11 +598,7 @@ void OpenGLVideo::PrepareFrame(VideoFrame *Frame, bool TopFieldFirst, FrameScanT
// bind correct textures
MythGLTexture* textures[MAX_VIDEO_TEXTURES];
uint numtextures = 0;
for (uint i = 0; i < inputtextures.size(); i++)
textures[numtextures++] = reinterpret_cast<MythGLTexture*>(inputtextures[i]);
if (deinterlacing)
for (uint i = 0; i < m_referenceTextures.size(); i++)
textures[numtextures++] = reinterpret_cast<MythGLTexture*>(m_referenceTextures[i]);
LoadTextures(deinterlacing, inputtextures, &textures[0], numtextures);

// render
m_render->DrawBitmap(textures, numtextures, m_frameBuffer, trect, vrect, m_shaders[program]);
Expand Down Expand Up @@ -637,11 +642,7 @@ void OpenGLVideo::PrepareFrame(VideoFrame *Frame, bool TopFieldFirst, FrameScanT
// bind correct textures
MythGLTexture* textures[MAX_VIDEO_TEXTURES];
uint numtextures = 0;
for (uint i = 0; i < inputtextures.size(); i++)
textures[numtextures++] = reinterpret_cast<MythGLTexture*>(inputtextures[i]);
if (deinterlacing)
for (uint i = 0; i < m_referenceTextures.size(); i++)
textures[numtextures++] = reinterpret_cast<MythGLTexture*>(m_referenceTextures[i]);
LoadTextures(deinterlacing, inputtextures, &textures[0], numtextures);

// draw
m_render->DrawBitmap(textures, numtextures, nullptr, trect, m_displayVideoRect, m_shaders[program]);
Expand All @@ -650,24 +651,40 @@ void OpenGLVideo::PrepareFrame(VideoFrame *Frame, bool TopFieldFirst, FrameScanT
m_render->logDebugMarker(LOC + "PREP_FRAME_END");
}

void OpenGLVideo::RotateTextures(void)
/// \brief Clear reference frames after a seek as they will contain old images.
void OpenGLVideo::ResetTextures(void)
{
if (m_referenceTexturesNeeded > 0)
m_referenceTexturesNeeded--;

if (m_referenceTextures.empty() || (m_referenceTextures.size() % m_inputTextures.size()))
return;
for (uint i = 0; i < m_inputTextures.size(); ++i)
m_inputTextures[i]->m_valid = false;
for (uint i = 0; i < m_prevTextures.size(); ++i)
m_prevTextures[i]->m_valid = false;
for (uint i = 0; i < m_nextTextures.size(); ++i)
m_nextTextures[i]->m_valid = false;
}

uint rotate = static_cast<uint>(m_inputTextures.size());
vector<MythVideoTexture*> newinputs, newrefs;
for (uint i = 0; i < rotate; ++i)
newinputs.push_back(m_referenceTextures[i]);
for (uint i = rotate; i < m_referenceTextures.size(); ++i)
newrefs.push_back(m_referenceTextures[i]);
for (uint i = 0; i < rotate; ++i)
newrefs.push_back(m_inputTextures[i]);
m_inputTextures = newinputs;
m_referenceTextures = newrefs;
void OpenGLVideo::LoadTextures(bool Deinterlacing, vector<MythVideoTexture*> &Current,
MythGLTexture **Textures, uint &TextureCount)
{
if (Deinterlacing && (m_nextTextures.size() == Current.size()) && (m_prevTextures.size() == Current.size()))
{
// if we are using reference frames, we want the current frame in the middle
// but next will be the first valid, followed by current...
uint count = Current.size();
vector<MythVideoTexture*> &current = Current[0]->m_valid ? Current : m_nextTextures;
vector<MythVideoTexture*> &prev = m_prevTextures[0]->m_valid ? m_prevTextures : current;

for (uint i = 0; i < count; ++i)
Textures[TextureCount++] = reinterpret_cast<MythGLTexture*>(prev[i]);
for (uint i = 0; i < count; ++i)
Textures[TextureCount++] = reinterpret_cast<MythGLTexture*>(current[i]);
for (uint i = 0; i < count; ++i)
Textures[TextureCount++] = reinterpret_cast<MythGLTexture*>(m_nextTextures[i]);
}
else
{
for (uint i = 0; i < Current.size(); ++i)
Textures[TextureCount++] = reinterpret_cast<MythGLTexture*>(Current[i]);
}
}

VideoFrameType OpenGLVideo::ProfileToType(const QString &Profile)
Expand Down
10 changes: 6 additions & 4 deletions mythtv/libs/libmythtv/openglvideo.h
Expand Up @@ -52,6 +52,7 @@ class OpenGLVideo : public QObject
void SetMasterViewport(QSize Size);
QSize GetVideoSize(void) const;
QString GetProfile() const;
void ResetTextures(void);

public slots:
void SetVideoRects(const QRect &DisplayVideoRect, const QRect &VideoRect);
Expand All @@ -60,7 +61,8 @@ class OpenGLVideo : public QObject

private:
bool CreateVideoShader(VideoShaderType Type, QString Deinterlacer = QString());
void RotateTextures(void);
void LoadTextures(bool Deinterlacing, vector<MythVideoTexture*> &Current,
MythGLTexture** Textures, uint &TextureCount);
void TearDownDeinterlacer(void);

bool m_valid;
Expand All @@ -79,12 +81,12 @@ class OpenGLVideo : public QObject
bool m_viewportControl; ///< Video has control over view port
QOpenGLShaderProgram* m_shaders[ShaderCount];
int m_shaderCost[ShaderCount];
vector<MythVideoTexture*> m_referenceTextures; ///< Up to 3 reference textures for filters
vector<MythVideoTexture*> m_inputTextures; ///< Textures with raw video data
vector<MythVideoTexture*> m_inputTextures; ///< Current textures with raw video data
vector<MythVideoTexture*> m_prevTextures; ///< Previous textures with raw video data
vector<MythVideoTexture*> m_nextTextures; ///< Next textures with raw video data
QOpenGLFramebufferObject* m_frameBuffer;
MythVideoTexture* m_frameBufferTexture;
QSize m_inputTextureSize; ///< Actual size of input texture(s)
uint m_referenceTexturesNeeded; ///< Number of reference textures still required
QOpenGLFunctions::OpenGLFeatures m_features; ///< Default features available from Qt
int m_extraFeatures; ///< OR'd list of extra, Myth specific features
bool m_resizing;
Expand Down
6 changes: 6 additions & 0 deletions mythtv/libs/libmythtv/videoout_opengl.cpp
Expand Up @@ -663,6 +663,12 @@ void VideoOutputOpenGL::Show(FrameScanType /*scan*/)
m_render->doneCurrent();
}
}
void VideoOutputOpenGL::ClearAfterSeek(void)
{
if (m_openGLVideo)
m_openGLVideo->ResetTextures();
VideoOutput::ClearAfterSeek();
}

/*! \brief Generate a list of supported OpenGL profiles.
*
Expand Down
7 changes: 4 additions & 3 deletions mythtv/libs/libmythtv/videoout_opengl.h
Expand Up @@ -15,8 +15,8 @@ class VideoOutputOpenGL : public VideoOutput
static QStringList GetAllowedRenderers(MythCodecID CodecId, const QSize &VideoDim);

explicit VideoOutputOpenGL(const QString &Profile = QString());
virtual ~VideoOutputOpenGL() override;
virtual void TearDown(void);
~VideoOutputOpenGL() override;
void TearDown(void);

// VideoOutput
bool Init(const QSize &VideoDim, const QSize &VideoDispDim, float Aspect,
Expand All @@ -25,7 +25,8 @@ class VideoOutputOpenGL : public VideoOutput
void PrepareFrame(VideoFrame *Frame, FrameScanType, OSD *Osd) override;
void ProcessFrame(VideoFrame *Frame, OSD *Osd, FilterChain *FilterList,
const PIPMap &PiPPlayers, FrameScanType Scan) override;
virtual void Show(FrameScanType ) override;
void Show(FrameScanType ) override;
void ClearAfterSeek(void) override;
bool InputChanged(const QSize &VideoDim, const QSize &VideoDispDim,
float Aspect, MythCodecID CodecId, bool &AspectOnly) override;
void UpdatePauseFrame(int64_t &DisplayTimecode) override;
Expand Down

0 comments on commit d03640a

Please sign in to comment.