Index: mythtv/libs/libmythtv/openglcontext.cpp =================================================================== --- mythtv/libs/libmythtv/openglcontext.cpp (revision 17692) +++ mythtv/libs/libmythtv/openglcontext.cpp (working copy) @@ -4,16 +4,57 @@ #include "util-opengl.h" + #define LOC QString("GLCtx: ") #define LOC_ERR QString("GLCtx, Error: ") +OpenGLContextLocker::OpenGLContextLocker(OpenGLContext *ctx) + : m_ctx(ctx) +{ + if (m_ctx) + m_ctx->MakeCurrent(true); +} +OpenGLContextLocker::~OpenGLContextLocker() +{ + if (m_ctx) + m_ctx->MakeCurrent(false); +} + +class MythGLTexture +{ + public: + MythGLTexture() : + m_type(GL_TEXTURE_2D), m_data(NULL), m_data_size(0), + m_data_type(GL_UNSIGNED_BYTE), m_data_fmt(GL_BGRA), + m_internal_fmt(GL_RGBA8), m_pbo(0), + m_filter(GL_LINEAR), m_wrap(GL_CLAMP_TO_EDGE), + m_size(0,0), m_vid_size(0,0) + { + } + + ~MythGLTexture() + { + } + + GLuint m_type; + unsigned char *m_data; + uint m_data_size; + GLuint m_data_type; + GLuint m_data_fmt; + GLuint m_internal_fmt; + GLuint m_pbo; + GLuint m_filter; + GLuint m_wrap; + QSize m_size; + QSize m_vid_size; +}; + class PrivateContext { public: PrivateContext() : m_glx_fbconfig(0), m_gl_window(0), m_glx_window(0), - m_glx_context(NULL), - m_texture_type(GL_TEXTURE_2D), m_textures_enabled(false), + m_glx_context(NULL), m_texture_type(0), m_vis_info(NULL), m_attr_list(NULL) { } @@ -27,21 +68,24 @@ GLXWindow m_glx_window; GLXContext m_glx_context; int m_texture_type; - bool m_textures_enabled; XVisualInfo *m_vis_info; int const *m_attr_list; - vector m_textures; + map m_textures; vector m_programs; vector m_framebuffers; + GLuint m_fence; }; -OpenGLContext::OpenGLContext() : +OpenGLContext::OpenGLContext(QMutex *lock) : m_priv(new PrivateContext()), m_display(NULL), m_screen_num(0), m_major_ver(1), m_minor_ver(2), m_extensions(QString::null), m_ext_supported(0), - m_visible(true), m_max_tex_size(0) + m_ext_used(0), + m_max_tex_size(0), m_viewport(0,0), + m_lock(lock), m_lock_level(0), + m_colour_control(false) { if (!init_opengl()) VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to initialize OpenGL support."); @@ -56,12 +100,18 @@ DeletePrograms(); DeleteTextures(); DeleteFrameBuffers(); + + Flush(true); + + if (m_priv->m_fence && + (m_ext_supported & kGLNVFence)) + { + gMythGLDeleteFencesNV(1, &(m_priv->m_fence)); + } } - glFlush(); + Flush(false); - MakeCurrent(false); - if (m_priv->m_glx_window) { X11S(glXDestroyWindow(m_display, m_priv->m_glx_window)); @@ -74,6 +124,8 @@ m_priv->m_gl_window = 0; } + MakeCurrent(false); + if (m_priv->m_glx_context) { X11S(glXDestroyContext(m_display, m_priv->m_glx_context)); @@ -89,22 +141,26 @@ void OpenGLContext::Hide(void) { + MakeCurrent(true); X11S(XUnmapWindow(m_display, m_priv->m_gl_window)); + MakeCurrent(false); } void OpenGLContext::Show(void) { + MakeCurrent(true); X11S(XMapWindow(m_display, m_priv->m_gl_window)); + MakeCurrent(false); } // locking ok bool OpenGLContext::Create( Display *XJ_disp, Window XJ_curwin, uint screen_num, - const QSize &display_visible_size, bool visible) + const QRect &display_visible, bool colour_control) { static bool debugged = false; - m_visible = visible; + m_colour_control = colour_control; m_display = XJ_disp; m_screen_num = screen_num; uint major, minor; @@ -180,7 +236,7 @@ } m_priv->m_gl_window = get_gl_window( - XJ_disp, XJ_curwin, m_priv->m_vis_info, display_visible_size, visible); + XJ_disp, XJ_curwin, m_priv->m_vis_info, display_visible); if (!m_priv->m_gl_window) { @@ -202,8 +258,7 @@ } } - VERBOSE(VB_PLAYBACK, LOC + QString("Created window%1 and context.") - .arg(m_visible ? "" : " (Offscreen)")); + VERBOSE(VB_PLAYBACK, LOC + QString("Created window and context.")); { MakeCurrent(true); @@ -237,46 +292,82 @@ MakeCurrent(false); } - int tex_type = get_gl_texture_rect_type(m_extensions); - m_priv->m_texture_type = (tex_type) ? tex_type : m_priv->m_texture_type; - m_ext_supported = - ((tex_type) ? kGLExtRect : 0) | + ((get_gl_texture_rect_type(m_extensions)) ? kGLExtRect : 0) | ((has_gl_fragment_program_support(m_extensions)) ? kGLExtFragProg : 0) | + ((has_gl_pixelbuffer_object_support(m_extensions)) ? + kGLExtPBufObj : 0) | ((has_gl_fbuffer_object_support(m_extensions)) ? kGLExtFBufObj : 0) | - ((minor >= 3) ? kGLXPBuffer : 0); + ((has_gl_nvfence_support(m_extensions)) ? kGLNVFence : 0) | + ((minor >= 3) ? kGLXPBuffer : 0) | kGLFinish; + m_ext_used = m_ext_supported; + + MakeCurrent(true); + + if (m_ext_used & kGLNVFence) + { + gMythGLGenFencesNV(1, &(m_priv->m_fence)); + if (m_priv->m_fence) + VERBOSE(VB_PLAYBACK, LOC + "Using GL_NV_fence"); + } + + Init2DState(); + MakeCurrent(false); + return true; } // locking ok bool OpenGLContext::MakeCurrent(bool current) { - bool ok; + bool ok = true; if (current) { - if (IsGLXSupported(1,3)) + m_lock->lock(); + if (m_lock_level == 0) { - X11S(ok = glXMakeCurrent(m_display, - m_priv->m_glx_window, - m_priv->m_glx_context)); + if (IsGLXSupported(1,3)) + { + X11S(ok = glXMakeContextCurrent(m_display, + m_priv->m_glx_window, + m_priv->m_glx_window, + m_priv->m_glx_context)); + } + else + { + X11S(ok = glXMakeCurrent(m_display, + m_priv->m_gl_window, + m_priv->m_glx_context)); + } } - else - { - X11S(ok = glXMakeCurrent(m_display, - m_priv->m_gl_window, - m_priv->m_glx_context)); - } + m_lock_level++; } else { - X11S(ok = glXMakeCurrent(m_display, None, NULL)); + m_lock_level--; + if (m_lock_level == 0) + { + if (IsGLXSupported(1,3)) + { + X11S(ok = glXMakeContextCurrent(m_display, None, None, NULL)); + } + else + { + X11S(ok = glXMakeCurrent(m_display, None, NULL)); + } + } + else if (m_lock_level < 0) + { + VERBOSE(VB_PLAYBACK, LOC_ERR + "Mis-matched calls to MakeCurrent"); + } + m_lock->unlock(); } if (!ok) - VERBOSE(VB_PLAYBACK, LOC + "Could not make context current."); + VERBOSE(VB_PLAYBACK, LOC_ERR + "Could not make context current."); return ok; } @@ -284,132 +375,383 @@ // locking ok void OpenGLContext::SwapBuffers(void) { - if (m_visible) - { - MakeCurrent(true); + MakeCurrent(true); + if (m_ext_used & kGLFinish) glFinish(); - if (IsGLXSupported(1,3)) - X11S(glXSwapBuffers(m_display, m_priv->m_glx_window)); - else - X11S(glXSwapBuffers(m_display, m_priv->m_gl_window)); - MakeCurrent(false); - } + if (IsGLXSupported(1,3)) + X11S(glXSwapBuffers(m_display, m_priv->m_glx_window)); + else + X11S(glXSwapBuffers(m_display, m_priv->m_gl_window)); + + MakeCurrent(false); } // locking ok -void OpenGLContext::Flush(void) +void OpenGLContext::Flush(bool use_fence) { - glFlush(); + MakeCurrent(true); + + if ((m_ext_used & kGLNVFence) && + m_priv->m_fence && use_fence) + { + gMythGLSetFenceNV(m_priv->m_fence, GL_ALL_COMPLETED_NV); + gMythGLFinishFenceNV(m_priv->m_fence); + } + else + { + glFlush(); + } + + MakeCurrent(false); } // locking ok -void OpenGLContext::EnableTextures(void) +void OpenGLContext::EnableTextures(uint tex, uint tex_type) { - if (!m_priv->m_textures_enabled) + MakeCurrent(true); + + int type = tex ? m_priv->m_textures[tex].m_type : tex_type; + + if (type != m_priv->m_texture_type) { - m_priv->m_textures_enabled = true; + if (m_priv->m_texture_type) + { + glDisable(m_priv->m_texture_type); + } + glEnable(type); + m_priv->m_texture_type = type; + } - MakeCurrent(true); - glEnable(GetTextureType()); - MakeCurrent(false); + MakeCurrent(false); +} + +void OpenGLContext::DisableTextures(void) +{ + MakeCurrent(true); + + glDisable(m_priv->m_texture_type); + m_priv->m_texture_type = 0; + + MakeCurrent(false); +} + +void OpenGLContext::UpdateTexture(uint tex, + const unsigned char *buf, + const int *offsets, + const int *pitches, + VideoFrameType fmt, + bool interlaced, + const unsigned char* alpha) +{ + MakeCurrent(true); + + MythGLTexture *tmp_tex = &m_priv->m_textures[tex]; + QSize size = tmp_tex->m_vid_size; + + EnableTextures(tex); + glBindTexture(tmp_tex->m_type, tex); + + if (tmp_tex->m_pbo) + { + void *pboMemory; + + gMythGLBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, tmp_tex->m_pbo); + gMythGLBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, + tmp_tex->m_data_size, NULL, GL_STREAM_DRAW); + + pboMemory = gMythGLMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, + GL_WRITE_ONLY); + + if (FMT_BGRA == fmt) + { + memcpy(pboMemory, buf, tmp_tex->m_data_size); + } + else if (interlaced) + { + pack_yv12interlaced(buf, (unsigned char *)pboMemory, + offsets, pitches, size); + } + else + { + pack_yv12alpha(buf, (unsigned char *)pboMemory, + offsets, pitches, size, alpha); + } + + gMythGLUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB); + + glTexSubImage2D(tmp_tex->m_type, 0, 0, 0, size.width(), size.height(), + tmp_tex->m_data_fmt, tmp_tex->m_data_type, 0); + + gMythGLBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); } + else + { + if (!tmp_tex->m_data) + { + unsigned char *scratch = new unsigned char[tmp_tex->m_data_size]; + if (scratch) + { + bzero(scratch, tmp_tex->m_data_size); + tmp_tex->m_data = scratch; + } + } + + if (tmp_tex->m_data) + { + const unsigned char *tmp = tmp_tex->m_data; + + if (FMT_BGRA == fmt) + { + tmp = buf; + } + else if (interlaced) + { + pack_yv12interlaced(buf, tmp, + offsets, pitches, size); + } + else + { + pack_yv12alpha(buf, tmp, offsets, + pitches, size, alpha); + } + + glTexSubImage2D(tmp_tex->m_type, 0, 0, 0, + size.width(), size.height(), + tmp_tex->m_data_fmt, tmp_tex->m_data_type, + tmp); + } + } + + MakeCurrent(false); } // locking ok -uint OpenGLContext::CreateTexture(void) +uint OpenGLContext::CreateTexture(QSize tot_size, QSize vid_size, + bool use_pbo, + uint type, uint data_type, + uint data_fmt, uint internal_fmt, + uint filter, uint wrap) { + if ((uint)tot_size.width() > m_max_tex_size || + (uint)tot_size.height() > m_max_tex_size) + return 0; + MakeCurrent(true); + EnableTextures(0, type); + GLuint tex; glGenTextures(1, &tex); - SetupTextureFilters(tex, GL_LINEAR); - m_priv->m_textures.push_back(tex); + glBindTexture(type, tex); + if (tex) + { + MythGLTexture *texture = new MythGLTexture(); + texture->m_type = type; + texture->m_data_type = data_type; + texture->m_data_fmt = data_fmt; + texture->m_internal_fmt = internal_fmt; + texture->m_size = tot_size; + texture->m_vid_size = vid_size; + texture->m_data_size = GetBufferSize(vid_size, data_fmt, data_type); + m_priv->m_textures[tex] = *texture; + + if (ClearTexture(tex) && m_priv->m_textures[tex].m_data_size) + { + SetTextureFilters(tex, filter, wrap); + if (use_pbo) + m_priv->m_textures[tex].m_pbo = CreatePBO(tex); + } + else + { + DeleteTexture(tex); + tex = 0; + } + + delete texture; + } + + Flush(true); + MakeCurrent(false); return tex; } // locking ok -bool OpenGLContext::SetupTexture(const QSize &size, uint tex, int filt) +uint OpenGLContext::GetBufferSize(QSize size, uint fmt, uint type) { - unsigned char *scratch = - new unsigned char[(size.width() * size.height() * 4) + 128]; + uint bytes; + uint bpp; - bzero(scratch, size.width() * size.height() * 4); + switch (fmt) + { + case GL_BGRA: + case GL_RGBA: + bpp = 4; + break; + default: + bpp =0; + } - GLint check; + switch (type) + { + case GL_UNSIGNED_BYTE: + bytes = sizeof(GLubyte); + break; + case GL_FLOAT: + bytes = sizeof(GLfloat); + break; + default: + bytes = 0; + } - MakeCurrent(true); - SetupTextureFilters(tex, filt); - glTexImage2D(GetTextureType(), 0, GL_RGBA8, size.width(), size.height(), - 0, GL_RGB , GL_UNSIGNED_BYTE, scratch); - glGetTexLevelParameteriv(GetTextureType(), 0, GL_TEXTURE_WIDTH, &check); - MakeCurrent(false); + if (!bpp || !bytes || size.width() < 1 || size.height() < 1) + return 0; - if (scratch) + return size.width() * size.height() * bpp * bytes; +} + +// locking ok +bool OpenGLContext::ClearTexture(uint tex) +{ + MythGLTexture *tmp = &m_priv->m_textures[tex]; + QSize size = tmp->m_size; + + uint tmp_size = GetBufferSize(size, tmp->m_data_fmt, tmp->m_data_type); + + if (!tmp_size) + return false; + + unsigned char *scratch = new unsigned char[tmp_size]; + + if (!scratch) + return false; + + bzero(scratch, tmp_size); + + GLint check; + if (tmp->m_type == GL_TEXTURE_1D) { - delete scratch; - scratch = NULL; + glTexImage1D(tmp->m_type, 0, tmp->m_internal_fmt, + size.width(), 0, + tmp->m_data_fmt , tmp->m_data_type, scratch); } + else + { + glTexImage2D(tmp->m_type, 0, tmp->m_internal_fmt, + size.width(), size.height(), 0, + tmp->m_data_fmt , tmp->m_data_type, scratch); + } + glGetTexLevelParameteriv(tmp->m_type, 0, GL_TEXTURE_WIDTH, &check); + delete [] scratch; + return (check == size.width()); } // locking ok -void OpenGLContext::SetupTextureFilters(uint tex, int filt) +void OpenGLContext::SetTextureFilters(uint tex, uint filt, uint wrap) { - glBindTexture(GetTextureType(), tex); - glTexParameteri(GetTextureType(), GL_TEXTURE_MIN_FILTER, filt); - glTexParameteri(GetTextureType(), GL_TEXTURE_MAG_FILTER, filt); - glTexParameteri(GetTextureType(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GetTextureType(), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (!m_priv->m_textures.count(tex)) + return; + + MakeCurrent(true); + + EnableTextures(tex); + + m_priv->m_textures[tex].m_filter = filt; + m_priv->m_textures[tex].m_wrap = wrap; + + uint type = m_priv->m_textures[tex].m_type; + + glBindTexture(type, tex); + glTexParameteri(type, GL_TEXTURE_MIN_FILTER, filt); + glTexParameteri(type, GL_TEXTURE_MAG_FILTER, filt); + glTexParameteri(type, GL_TEXTURE_WRAP_S, wrap); + if (type != GL_TEXTURE_1D) + glTexParameteri(type, GL_TEXTURE_WRAP_T, wrap); + + MakeCurrent(false); } // locking ok void OpenGLContext::DeleteTexture(uint tex) { + if (!m_priv->m_textures.count(tex)) + return; + MakeCurrent(true); - vector::iterator it; - for (it = m_priv->m_textures.begin(); it !=m_priv->m_textures.end(); it++) + GLuint gltex = tex; + glDeleteTextures(1, &gltex); + + if (m_priv->m_textures[tex].m_data) { - if (*(it) == tex) - { - GLuint gltex = tex; - glDeleteTextures(1, &gltex); - m_priv->m_textures.erase(it); - break; - } + delete m_priv->m_textures[tex].m_data; } + if (m_priv->m_textures[tex].m_pbo) + { + gMythGLDeleteBuffersARB(1, &(m_priv->m_textures[tex].m_pbo)); + } + + m_priv->m_textures.erase(tex); + + Flush(true); + MakeCurrent(false); } // locking ok void OpenGLContext::DeleteTextures(void) { - MakeCurrent(true); + map::iterator it; + for (it = m_priv->m_textures.begin(); it !=m_priv->m_textures.end(); it++) + { + GLuint gltex = it->first; + glDeleteTextures(1, &gltex); - vector::iterator it; - for (it = m_priv->m_textures.begin(); it !=m_priv->m_textures.end(); it++) - glDeleteTextures(1, &(*(it))); + if (it->second.m_data) + { + delete it->second.m_data; + } + + if (it->second.m_pbo) + { + gltex = it->second.m_pbo; + gMythGLDeleteBuffersARB(1, &gltex); + } + } m_priv->m_textures.clear(); - MakeCurrent(false); + Flush(true); } -int OpenGLContext::GetTextureType(void) const +void OpenGLContext::GetTextureType(uint ¤t, bool &rect) { - return m_priv->m_texture_type; + uint type = get_gl_texture_rect_type(m_extensions); + if (type) + { + rect = true; + current = type; + return; + } + + rect = false; + return; } // locking ok bool OpenGLContext::CreateFragmentProgram(const QString &program, uint &fp) { bool success = true; + + if (!(m_ext_used & kGLExtFragProg)) + return false; + GLint error; MakeCurrent(true); @@ -449,6 +791,8 @@ gMythGLDeleteProgramsARB(1, &glfp); } + Flush(true); + MakeCurrent(false); fp = glfp; @@ -473,53 +817,66 @@ } } + Flush(true); + MakeCurrent(false); } void OpenGLContext::BindFragmentProgram(uint fp) { + MakeCurrent(true); gMythGLBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, fp); + MakeCurrent(false); } void OpenGLContext::InitFragmentParams( uint fp, float a, float b, float c, float d) { + MakeCurrent(true); gMythGLProgramEnvParameter4fARB( GL_FRAGMENT_PROGRAM_ARB, fp, a, b, c, d); + MakeCurrent(false); } void OpenGLContext::DeletePrograms(void) { - MakeCurrent(true); - vector::iterator it; for (it = m_priv->m_programs.begin(); it != m_priv->m_programs.end(); it++) gMythGLDeleteProgramsARB(1, &(*(it))); m_priv->m_programs.clear(); - MakeCurrent(false); + Flush(true); } // locking ok -bool OpenGLContext::CreateFrameBuffer(uint &fb, uint tex, const QSize &size) +bool OpenGLContext::CreateFrameBuffer(uint &fb, uint tex) { + if (!(m_ext_used & kGLExtFBufObj)) + return false; + + if (!m_priv->m_textures.count(tex)) + return false; + + MythGLTexture *tmp = &m_priv->m_textures[tex]; + QSize size = tmp->m_size; GLuint glfb; MakeCurrent(true); + glCheck(); - SetupTextureFilters(tex, GL_LINEAR); + EnableTextures(tex); glPushAttrib(GL_VIEWPORT_BIT); glViewport(0, 0, size.width(), size.height()); gMythGLGenFramebuffersEXT(1, &glfb); gMythGLBindFramebufferEXT(GL_FRAMEBUFFER_EXT, glfb); - glBindTexture(GetTextureType(), tex); - glTexImage2D(GetTextureType(), 0, GL_RGBA8, + glBindTexture(tmp->m_type, tex); + glTexImage2D(tmp->m_type, 0, tmp->m_internal_fmt, (GLint) size.width(), (GLint) size.height(), 0, - GL_RGB, GL_UNSIGNED_BYTE, NULL); + tmp->m_data_fmt, tmp->m_data_type, NULL); gMythGLFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - GetTextureType(), tex, 0); + tmp->m_type, tex, 0); GLenum status; status = gMythGLCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); @@ -575,6 +932,9 @@ else gMythGLDeleteFramebuffersEXT(1, &glfb); + Flush(true); + + glCheck(); MakeCurrent(false); fb = glfb; @@ -600,13 +960,13 @@ } } + Flush(true); + MakeCurrent(false); } void OpenGLContext::DeleteFrameBuffers(void) { - MakeCurrent(true); - vector::iterator it; for (it = m_priv->m_framebuffers.begin(); it != m_priv->m_framebuffers.end(); it++) @@ -615,13 +975,15 @@ } m_priv->m_framebuffers.clear(); - MakeCurrent(false); + Flush(true); } // locking ok void OpenGLContext::BindFramebuffer(uint fb) { + MakeCurrent(true); gMythGLBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); + MakeCurrent(false); } bool OpenGLContext::IsGLXSupported( @@ -636,3 +998,171 @@ return false; } + +void OpenGLContext::Init2DState(void) +{ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glDisable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // for gl osd + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDisable(GL_CULL_FACE); + glShadeModel(GL_FLAT); + glDisable(GL_POLYGON_SMOOTH); + glDisable(GL_LINE_SMOOTH); + glDisable(GL_POINT_SMOOTH); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + Flush(true); +} + +void OpenGLContext::SetViewPort(const QSize &size) +{ + if (size.width() == m_viewport.width() && + size.height() == m_viewport.height()) + return; + + MakeCurrent(true); + + m_viewport = size; + + glViewport(0, 0, size.width(), size.height()); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, size.width() - 1, + 0, size.height() - 1, 1, -1); // aargh... + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + MakeCurrent(false); +} + +uint OpenGLContext::CreatePBO(uint tex) +{ + if (!(m_ext_used & kGLExtPBufObj)) + return 0; + + if (!m_priv->m_textures.count(tex)) + return 0; + + MythGLTexture *tmp = &m_priv->m_textures[tex]; + + gMythGLBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); + glTexImage2D(tmp->m_type, 0, tmp->m_internal_fmt, + tmp->m_size.width(), tmp->m_size.height(), 0, + tmp->m_data_fmt, tmp->m_data_type, NULL); + + GLuint tmp_pbo; + gMythGLGenBuffersARB(1, &tmp_pbo); + + gMythGLBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0); + + Flush(true); + + return tmp_pbo; +} + +uint OpenGLContext::CreateHelperTexture(void) +{ + MakeCurrent(true); + + uint width = m_max_tex_size; + + uint tmp_tex = CreateTexture(QSize(width, 1), QSize(width, 1), + false, + GL_TEXTURE_1D, GL_FLOAT, + GL_RGBA, GL_RGBA16, + GL_NEAREST, GL_REPEAT); + + if (!tmp_tex) + { + DeleteTexture(tmp_tex); + return 0; + } + + float *buf = NULL; + buf = new float[m_priv->m_textures[tmp_tex].m_data_size]; + float *ref = buf; + + for (uint i = 0; i < width; i++) + { + float x = (((float)i) + 0.5f) / (float)width; + store_bicubic_weights(x, ref); + ref += 4; + } + store_bicubic_weights(0, buf); + store_bicubic_weights(1, &buf[(width - 1) << 2]); + + EnableTextures(tmp_tex); + glBindTexture(m_priv->m_textures[tmp_tex].m_type, tmp_tex); + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA16, width, 0, GL_RGBA, GL_FLOAT, buf); + + VERBOSE(VB_PLAYBACK, LOC + + QString("Created bicubic helper texture (%1 samples)") + .arg(width)); + + delete [] buf; + + MakeCurrent(false); + + return tmp_tex; +} + +int OpenGLContext::SetPictureAttribute( + PictureAttribute attribute, int newValue) +{ + if (!m_colour_control) + return -1; + + MakeCurrent(true); + + int ret = -1; + switch (attribute) + { + case kPictureAttribute_Brightness: + ret = newValue; + pictureAttribs[attribute] = (newValue * 0.02f) - 0.5f; + break; + case kPictureAttribute_Contrast: + case kPictureAttribute_Colour: + ret = newValue; + pictureAttribs[attribute] = (newValue * 0.02f); + break; + case kPictureAttribute_Hue: // not supported yet... + break; + default: + break; + } + + MakeCurrent(false); + + return ret; +} + +PictureAttributeSupported +OpenGLContext::GetSupportedPictureAttributes(void) const +{ + return (!m_colour_control) ? + kPictureAttributeSupported_None : + (PictureAttributeSupported) + (kPictureAttributeSupported_Brightness | + kPictureAttributeSupported_Contrast | + kPictureAttributeSupported_Colour); +} + +void OpenGLContext::SetColourParams(void) +{ + if (!m_colour_control) + return; + + MakeCurrent(true); + + InitFragmentParams(0, + pictureAttribs[kPictureAttribute_Brightness], + pictureAttribs[kPictureAttribute_Contrast], + pictureAttribs[kPictureAttribute_Colour], + 0.5f); + + MakeCurrent(false); +} + Index: mythtv/libs/libmythtv/util-opengl.h =================================================================== --- mythtv/libs/libmythtv/util-opengl.h (revision 17692) +++ mythtv/libs/libmythtv/util-opengl.h (working copy) @@ -8,6 +8,7 @@ // MythTV headers #include "mythcontext.h" #include "util-x11.h" +#include "frame.h" // GLX headers #define GLX_GLXEXT_PROTOTYPES @@ -17,13 +18,6 @@ // Qt headers #include -#ifndef APIENTRY -#define APIENTRY -#endif -#ifndef APIENTRYP -#define APIENTRYP APIENTRY * -#endif - #ifndef GL_TEXTURE_RECTANGLE_ARB #define GL_TEXTURE_RECTANGLE_ARB 0x84F5 #endif @@ -40,10 +34,6 @@ #define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT 0x8CD8 #endif -#ifndef GL_FRAGMENT_PROGRAM_ARB -#define GL_FRAGMENT_PROGRAM_ARB 0x8804 -#endif - // Not all platforms with OpenGL that MythTV supports have the // GL_EXT_framebuffer_object extension so we need to define these.. #ifndef GL_FRAMEBUFFER_EXT @@ -77,6 +67,9 @@ #define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD #endif +#ifndef GL_NV_fence +#define GL_ALL_COMPLETED_NV 0x84F2 +#endif #ifndef GLX_VERSION_1_3 typedef XID GLXPbuffer; @@ -113,8 +106,7 @@ Window get_gl_window(Display *XJ_disp, Window XJ_curwin, XVisualInfo *visinfo, - const QSize &window_size, - bool map_window); + const QRect &window_rect); GLXWindow get_glx_window(Display *XJ_disp, GLXFBConfig glx_fbconfig, @@ -123,18 +115,29 @@ GLXPbuffer glx_pbuffer, const QSize &window_size); -void copy_pixels_to_texture(const unsigned char *buf, - int buffer_format, - const QSize &buffer_size, - int texture, - int texture_type); +void pack_yv12alpha(const unsigned char *source, + const unsigned char *dest, + const int *offsets, + const int *pitches, + const QSize size, + const unsigned char *alpha = NULL); +void pack_yv12interlaced(const unsigned char *source, + const unsigned char *dest, + const int *offsets, + const int *pitches, + const QSize size); + +void store_bicubic_weights(float x, float *dst); + __GLXextFuncPtr get_gl_proc_address(const QString &procName); int get_gl_texture_rect_type(const QString &extensions); bool has_gl_fbuffer_object_support(const QString &extensions); bool has_gl_fragment_program_support(const QString &extensions); bool has_glx_video_sync_support(const QString &glx_extensions); +bool has_gl_pixelbuffer_object_support(const QString &extensions); +bool has_gl_nvfence_support(const QString &extensions); extern QString gMythGLExtensions; extern uint gMythGLExtSupported; @@ -146,6 +149,13 @@ extern PFNGLDELETEPROGRAMSARBPROC gMythGLDeleteProgramsARB; extern PFNGLGETPROGRAMIVARBPROC gMythGLGetProgramivARB; +extern PFNGLMAPBUFFERPROC gMythGLMapBufferARB; +extern PFNGLBINDBUFFERARBPROC gMythGLBindBufferARB; +extern PFNGLGENBUFFERSARBPROC gMythGLGenBuffersARB; +extern PFNGLBUFFERDATAARBPROC gMythGLBufferDataARB; +extern PFNGLUNMAPBUFFERARBPROC gMythGLUnmapBufferARB; +extern PFNGLDELETEBUFFERSARBPROC gMythGLDeleteBuffersARB; + // Not all platforms with OpenGL that MythTV supports have the // GL_EXT_framebuffer_object extension so we need to define these.. typedef void (APIENTRYP MYTH_GLGENFRAMEBUFFERSEXTPROC) @@ -169,6 +179,10 @@ extern PFNGLXGETVIDEOSYNCSGIPROC gMythGLXGetVideoSyncSGI; extern PFNGLXWAITVIDEOSYNCSGIPROC gMythGLXWaitVideoSyncSGI; +extern PFNGLGENFENCESNVPROC gMythGLGenFencesNV; +extern PFNGLDELETEFENCESNVPROC gMythGLDeleteFencesNV; +extern PFNGLSETFENCENVPROC gMythGLSetFenceNV; +extern PFNGLFINISHFENCENVPROC gMythGLFinishFenceNV; #endif // USING_OPENGL #endif // _UTIL_OPENGL_H_ Index: mythtv/libs/libmythtv/openglvideo.h =================================================================== --- mythtv/libs/libmythtv/openglvideo.h (revision 17692) +++ mythtv/libs/libmythtv/openglvideo.h (working copy) @@ -9,6 +9,7 @@ #include #include "videooutbase.h" +#include "videoouttypes.h" enum OpenGLFilterType { @@ -18,25 +19,13 @@ kGLFilterYUV2RGB, kGLFilterYUV2RGBA, - // Frame rate preserving deinterlacers - kGLFilterLinearBlendDeint, - kGLFilterKernelDeint, - kGLFilterOneFieldDeint, - - // Frame rate doubling deinterlacers - kGLFilterBobDeintDFR, - kGLFilterLinearBlendDeintDFR, - kGLFilterKernelDeintDFR, - kGLFilterFieldOrderDFR, - kGLFilterOneFieldDeintDFR, - // Frame scaling/resizing filters kGLFilterResize, + kGLFilterBicubic, }; enum DisplayBuffer { - kNoBuffer = 0, // disable filter kDefaultBuffer, kFrameBufferObject }; @@ -54,28 +43,25 @@ OpenGLVideo(); ~OpenGLVideo(); - bool Init(OpenGLContext *glcontext, bool colour_control, bool onscreen, - QSize video_size, QRect visible_rect, - QRect video_rect, QRect frame_rect, - bool viewport_control, bool osd = FALSE); - bool ReInit(OpenGLContext *gl, bool colour_control, bool onscreen, - QSize video_size, QRect visible_rect, - QRect video_rect, QRect frame_rect, - bool viewport_control, bool osd = FALSE); + bool Init(OpenGLContext *glcontext, bool colour_control, + QSize videoDim, QRect displayVisibleRect, + QRect displayVideoRect, QRect videoRect, + bool viewport_control, QString options, bool osd = FALSE); - void UpdateInputFrame(const VideoFrame *frame); + void UpdateInputFrame(const VideoFrame *frame, bool soft_bob = FALSE); void UpdateInput(const unsigned char *buf, const int *offsets, - uint texture_index, int format, QSize size); + int format, QSize size, + const unsigned char *alpha); bool AddFilter(const QString &filter) { return AddFilter(StringToFilter(filter)); } bool RemoveFilter(const QString &filter) { return RemoveFilter(StringToFilter(filter)); } - bool AddDeinterlacer(const QString &filter); + bool AddDeinterlacer(const QString &deinterlacer); void SetDeinterlacing(bool deinterlacing); QString GetDeinterlacer(void) const - { return FilterToString(GetDeintFilter()); }; + { return hardwareDeinterlacer; } void SetSoftwareDeinterlacer(const QString &filter) { softwareDeinterlacer = QDeepCopy(filter); }; @@ -84,67 +70,75 @@ void SetMasterViewport(QSize size) { masterViewportSize = size; } QSize GetViewPort(void) const { return viewportSize; } - void SetVideoRect(const QRect &vidrect, const QRect &framerect) - { videoRect = vidrect; frameRect = framerect;} - QSize GetVideoSize(void) const { return videoSize; } + void SetVideoRect(const QRect &dispvidrect, const QRect &vidrect) + { display_video_rect = dispvidrect; video_rect = vidrect;} + QSize GetVideoSize(void) const { return actual_video_dim;} void SetVideoResize(const QRect &rect); void DisableVideoResize(void); - int SetPictureAttribute(PictureAttribute attributeType, int newValue); - PictureAttributeSupported GetSupportedPictureAttributes(void) const; private: void Teardown(void); void SetViewPort(const QSize &new_viewport_size); - void SetViewPortPrivate(const QSize &new_viewport_size); bool AddFilter(OpenGLFilterType filter); bool RemoveFilter(OpenGLFilterType filter); + void CheckResize(bool deinterlacing); bool OptimiseFilters(void); - OpenGLFilterType GetDeintFilter(void) const; - bool AddFrameBuffer(uint &framebuffer, uint &texture, QSize size); - uint AddFragmentProgram(OpenGLFilterType name); - uint CreateVideoTexture(QSize size, QSize &tex_size); - QString GetProgramString(OpenGLFilterType filter); + bool AddFrameBuffer(uint &framebuffer, QSize fb_size, + uint &texture, QSize vid_size); + uint AddFragmentProgram(OpenGLFilterType name, + QString deint = QString::null, + FrameScanType field = kScan_Progressive); + uint CreateVideoTexture(QSize size, QSize &tex_size, + bool use_pbo = false); + QString GetProgramString(OpenGLFilterType filter, + QString deint = QString::null, + FrameScanType field = kScan_Progressive); void CalculateResize(float &left, float &top, float &right, float &bottom); static QString FilterToString(OpenGLFilterType filter); static OpenGLFilterType StringToFilter(const QString &filter); void ShutDownYUV2RGB(void); - void SetViewPort(bool last_stage); - void InitOpenGL(void); QSize GetTextureSize(const QSize &size); void SetFiltering(void); - void Rotate(vector *target); - void SetTextureFilters(vector *textures, int filt); + void RotateTextures(void); + void SetTextureFilters(vector *textures, int filt, int clamp); + void DeleteTextures(vector *textures); + void TearDownDeinterlacer(void); + uint ParseOptions(QString options); OpenGLContext *gl_context; - QSize videoSize; + QSize video_dim; + QSize actual_video_dim; QSize viewportSize; QSize masterViewportSize; - QRect visibleRect; - QRect videoRect; - QRect frameRect; + QRect display_visible_rect; + QRect display_video_rect; + QRect video_rect; QRect frameBufferRect; - bool invertVideo; QString softwareDeinterlacer; + QString hardwareDeinterlacer; bool hardwareDeinterlacing; bool useColourControl; bool viewportControl; - uint frameBuffer; - uint frameBufferTexture; + vector referenceTextures; vector inputTextures; QSize inputTextureSize; glfilt_map_t filters; long long currentFrameNum; bool inputUpdated; + bool textureRects; + uint textureType; + uint helperTexture; + OpenGLFilterType defaultUpsize; - QSize convertSize; - unsigned char *convertBuf; + QSize convertSize; + unsigned char *convertBuf; - bool videoResize; - QRect videoResizeRect; + bool videoResize; + QRect videoResizeRect; - float pictureAttribs[kPictureAttribute_MAX]; + uint gl_features; }; #else // if !USING_OPENGL_VIDEO @@ -155,16 +149,13 @@ OpenGLVideo() { } ~OpenGLVideo() { } - bool Init(OpenGLContext*, bool, bool, QSize, QRect, - QRect, QRect, bool, bool osd = false) + bool Init(OpenGLContext*, bool, QSize, QRect, + QRect, QRect, bool, QString, bool osd = false) { (void) osd; return false; } - bool ReInit(OpenGLContext*, bool, bool, QSize, QRect, - QRect, QRect, bool, bool osd = false) - { (void) osd; return false; } - void UpdateInputFrame(const VideoFrame*) { } - void UpdateInput(const unsigned char*, const int*, uint, int, QSize) { } + void UpdateInput(const unsigned char*, const int*, + int, QSize, unsigned char* = NULL) { } bool AddFilter(const QString&) { return false; } bool RemoveFilter(const QString&) { return false; } @@ -182,9 +173,6 @@ QSize GetVideoSize(void) const { return QSize(0,0); } void SetVideoResize(const QRect&) { } void DisableVideoResize(void) { } - int SetPictureAttribute(PictureAttribute, int) { return -1; } - PictureAttributeSupported GetSupportedPictureAttributes(void) const - { return kPictureAttributeSupported_None; } }; #endif // !USING_OPENGL_VIDEO Index: mythtv/libs/libmythtv/openglcontext.h =================================================================== --- mythtv/libs/libmythtv/openglcontext.h (revision 17692) +++ mythtv/libs/libmythtv/openglcontext.h (working copy) @@ -11,7 +11,25 @@ // MythTV headers #include "util-x11.h" +#include "frame.h" +#include "videooutbase.h" +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif +#ifndef GL_UNSIGNED_BYTE +#define GL_UNSIGNED_BYTE 0x1401 +#endif +#ifndef GL_RGBA8 +#define GL_RGBA8 0x8058 +#endif +#ifndef GL_LINEAR +#define GL_LINEAR 0x2601 +#endif +#ifndef GL_CLAMP_TO_EDGE +#define GL_CLAMP_TO_EDGE 0x812F +#endif + class OpenGLVideo; class PrivateContext; @@ -21,59 +39,96 @@ kGLExtFragProg = 0x02, kGLExtFBufObj = 0x04, kGLXPBuffer = 0x08, + kGLExtPBufObj = 0x10, + kGLNVFence = 0x20, + kGLFinish = 0x40, + kGLMaxFeat = 0x80, } GLFeatures; +class OpenGLContext; + +class OpenGLContextLocker +{ + public: + OpenGLContextLocker(OpenGLContext *ctx); + ~OpenGLContextLocker(); + + private: + OpenGLContext *m_ctx; +}; + #ifdef USING_OPENGL class OpenGLContext { public: - OpenGLContext(); + OpenGLContext(QMutex *lock); ~OpenGLContext(); bool Create(Display *display, Window window, uint screen_num, - const QSize &display_visible_size, bool visible); + const QRect &display_visible, bool colour_control = false); + void SetViewPort(const QSize &size); void Hide(void); void Show(void); bool MakeCurrent(bool current); void SwapBuffers(void); - void Flush(void); + void Flush(bool use_fence); - uint GetMaxTexSize(void) const { return m_max_tex_size; } uint GetScreenNum(void) const { return m_screen_num; } - uint CreateTexture(void); - bool SetupTexture(const QSize &size, uint tex, int filt); - void SetupTextureFilters(uint tex, int filt); + void UpdateTexture(uint tex, const unsigned char *buf, + const int *offsets, + const int *pitches, + VideoFrameType fmt, + bool interlaced = FALSE, + const unsigned char* alpha = NULL); + uint CreateTexture(QSize tot_size, QSize vid_size, + bool use_pbo, uint type, + uint data_type = GL_UNSIGNED_BYTE, + uint data_fmt = GL_BGRA, + uint internal_fmt = GL_RGBA8, + uint filter = GL_LINEAR, + uint wrap = GL_CLAMP_TO_EDGE); + void SetTextureFilters(uint tex, uint filt, uint wrap); void DeleteTexture(uint tex); - int GetTextureType(void) const; - void EnableTextures(void); + void GetTextureType(uint ¤t, bool &rect); + void EnableTextures(uint type, uint tex_type = 0); + void DisableTextures(void); bool CreateFragmentProgram(const QString &program, uint &prog); void DeleteFragmentProgram(uint prog); void BindFragmentProgram(uint fp); void InitFragmentParams(uint fp, float a, float b, float c, float d); - bool CreateFrameBuffer(uint &fb, uint tex, const QSize &size); + bool CreateFrameBuffer(uint &fb, uint tex); void DeleteFrameBuffer(uint fb); void BindFramebuffer(uint fb); + uint GetFeatures(void) { return m_ext_supported; } + void SetFeatures(uint features) { m_ext_used = features; } - bool IsFeatureSupported(GLFeatures feature) const - { return m_ext_supported & feature; } - static bool IsGLXSupported(Display *display, uint major, uint minor); + int SetPictureAttribute(PictureAttribute attributeType, int newValue); + PictureAttributeSupported GetSupportedPictureAttributes(void) const; + void SetColourParams(void); + uint CreateHelperTexture(void); + private: + void Init2DState(void); bool IsGLXSupported(uint major, uint minor) const { return (m_major_ver > major) || ((m_major_ver == major) && (m_minor_ver >= minor)); } + uint CreatePBO(uint tex); + void DeleteTextures(void); void DeletePrograms(void); void DeleteFrameBuffers(void); + uint GetBufferSize(QSize size, uint fmt, uint type); + bool ClearTexture(uint tex); PrivateContext *m_priv; @@ -83,8 +138,14 @@ uint m_minor_ver; QString m_extensions; uint m_ext_supported; - bool m_visible; + uint m_ext_used; uint m_max_tex_size; + QSize m_viewport; + QMutex *m_lock; + int m_lock_level; + bool m_colour_control; + + float pictureAttribs[kPictureAttribute_MAX]; }; #else // if !USING_OPENGL @@ -92,37 +153,51 @@ class OpenGLContext { public: - OpenGLContext() { } + OpenGLContext(QMutex*) { } ~OpenGLContext() { } - bool Create(Display*, Window, uint, const QSize&, bool) { return false; } + bool Create(Display*, Window, uint, const Rect&, bool = false) + { return false; } + void SetViewPort(const QSize&) { } + void Hide(void) { } + void Show(void) { } bool MakeCurrent(bool) { return false; } void SwapBuffers(void) { } - void Flush(void) { } + void Flush(bool) { } - uint GetMaxTexSize(void) const { return 0; } uint GetScreenNum(void) const { return 0; } - uint CreateTexture(void) { return 0; } - bool SetupTexture(const QSize&, uint, int) { return false; } - void SetupTextureFilters(uint, int) { } + void UpdateTexture(uint, const unsigned char*, + const int *, const int *, + VideoFrameType, bool = FALSE, + const unsigned char* = NULL) { } + uint CreateTexture(QSize, QSize, bool, uint, + uint = 0, uint = 0, uint = 0, + uint = 0, uint = 0) { return 0; } + void SetTextureFilters(uint, uint, uint) { } void DeleteTexture(uint) { } - int GetTextureType(void) const { return 0; } - void EnableTextures(void) { } + void GetTextureType(uint&, bool&) { } + void EnableTextures(uint, uint = 0) { } bool CreateFragmentProgram(const QString&, uint&) { return false; } void DeleteFragmentProgram(uint) { } void BindFragmentProgram(uint) { } void InitFragmentParams(uint, float, float, float, float) { } - bool CreateFrameBuffer(uint&, uint, const QSize&) { return false; } + bool CreateFrameBuffer(uint&, uint) { return false; } void DeleteFrameBuffer(uint); void BindFramebuffer(uint); - bool IsFeatureSupported(GLFeatures) const { return false; } + uint GetFeatures(void) { return 0; } + void SetFeatures(uint) { } + static bool IsGLXSupported(Display*, uint, uint) { return false; } - static bool IsGLXSupported(Display*, uint, uint) { return false; } + int SetPictureAttribute(PictureAttribute, int) { return -1; } + PictureAttributeSupported GetSupportedPictureAttributes(void) const + { return kPictureAttributeSupported_None; } + void SetColourParams(void); + uint CreateHelperTexture(void); }; #endif //!USING_OPENGL Index: mythtv/libs/libmythtv/frame.h =================================================================== --- mythtv/libs/libmythtv/frame.h (revision 17692) +++ mythtv/libs/libmythtv/frame.h (working copy) @@ -21,7 +21,7 @@ FMT_ARGB32, FMT_RGBA32, FMT_YUV422P, - FMT_ALPHA, + FMT_BGRA, } VideoFrameType; typedef struct VideoFrame_ Index: mythtv/libs/libmythtv/openglvideo.cpp =================================================================== --- mythtv/libs/libmythtv/openglvideo.cpp (revision 17692) +++ mythtv/libs/libmythtv/openglvideo.cpp (working copy) @@ -26,35 +26,34 @@ class OpenGLFilter { public: - GLuint fragmentProgram; + vector fragmentPrograms; uint numInputs; - bool rotateFrameBuffers; vector frameBuffers; vector frameBufferTextures; DisplayBuffer outputBuffer; }; OpenGLVideo::OpenGLVideo() : - gl_context(NULL), videoSize(0,0), - viewportSize(0,0), masterViewportSize(0,0), - visibleRect(0,0,0,0), videoRect(0,0,0,0), - frameRect(0,0,0,0), - frameBufferRect(0,0,0,0), invertVideo(false), - softwareDeinterlacer(QString::null), - hardwareDeinterlacing(false), + gl_context(NULL), video_dim(0,0), + actual_video_dim(0,0), viewportSize(0,0), + masterViewportSize(0,0), display_visible_rect(0,0,0,0), + display_video_rect(0,0,0,0), video_rect(0,0,0,0), + frameBufferRect(0,0,0,0), softwareDeinterlacer(QString::null), + hardwareDeinterlacer(QString::null), hardwareDeinterlacing(false), useColourControl(false), viewportControl(false), - frameBuffer(0), frameBufferTexture(0), inputTextureSize(0,0), currentFrameNum(0), - inputUpdated(false), - + inputUpdated(false), + textureRects(false), textureType(GL_TEXTURE_2D), + helperTexture(0), defaultUpsize(kGLFilterResize), convertSize(0,0), convertBuf(NULL), - - videoResize(false), videoResizeRect(0,0,0,0) + videoResize(false), videoResizeRect(0,0,0,0), + gl_features(0) { } OpenGLVideo::~OpenGLVideo() { + OpenGLContextLocker ctx_lock(gl_context); Teardown(); } @@ -63,55 +62,45 @@ { ShutDownYUV2RGB(); - gl_context->MakeCurrent(true); + if (helperTexture) + gl_context->DeleteTexture(helperTexture); + helperTexture = 0; - if (frameBuffer) - gl_context->DeleteFrameBuffer(frameBuffer); + DeleteTextures(&inputTextures); + DeleteTextures(&referenceTextures); - if (frameBufferTexture) - gl_context->DeleteTexture(frameBufferTexture); - - for (uint i = 0; i < inputTextures.size(); i++) - gl_context->DeleteTexture(inputTextures[i]); - inputTextures.clear(); - if (!filters.empty()) { glfilt_map_t::iterator it; - for (it = filters.begin(); it != filters.end(); ++it) + for (it = filters.begin(); it != filters.end(); it++) { - if (it->second->fragmentProgram) - gl_context->DeleteFragmentProgram(it->second->fragmentProgram); - vector temp = it->second->frameBuffers; - for (uint i = 0; i < temp.size(); i++) - gl_context->DeleteFrameBuffer(temp[i]); - temp = it->second->frameBufferTextures; - for (uint i = 0; i < temp.size(); i++) - gl_context->DeleteTexture((temp[i])); + RemoveFilter(it->first); } } filters.clear(); - - gl_context->MakeCurrent(false); } // locking ok bool OpenGLVideo::Init(OpenGLContext *glcontext, bool colour_control, - bool onscreen, QSize video_size, QRect visible_rect, - QRect video_rect, QRect frame_rect, - bool viewport_control, bool osd) + QSize videoDim, QRect displayVisibleRect, + QRect displayVideoRect, QRect videoRect, + bool viewport_control, QString options, bool osd) { gl_context = glcontext; - videoSize = video_size; - visibleRect = visible_rect; - videoRect = video_rect; - frameRect = frame_rect; - masterViewportSize = QSize(1920, 1080); + if (!gl_context) + return false; - QSize rect = GetTextureSize(videoSize); + OpenGLContextLocker ctx_lock(gl_context); - frameBufferRect = QRect(QPoint(0,0), rect); - invertVideo = true; + actual_video_dim = videoDim; + video_dim = videoDim; + if (video_dim.height() == 1088) + video_dim.setHeight(1080); + display_visible_rect = displayVisibleRect; + display_video_rect = displayVideoRect; + video_rect = videoRect; + masterViewportSize = QSize(1920, 1080); + frameBufferRect = QRect(QPoint(0,0), video_dim); softwareDeinterlacer = ""; hardwareDeinterlacing = false; useColourControl = colour_control; @@ -120,55 +109,54 @@ convertSize = QSize(0,0); videoResize = false; videoResizeRect = QRect(0,0,0,0); - frameBuffer = 0; currentFrameNum = -1; inputUpdated = false; - if (!onscreen) - { - QSize fb_size = GetTextureSize(visibleRect.size()); - if (!AddFrameBuffer(frameBuffer, frameBufferTexture, fb_size)) - return false; - } + gl_features = ParseOptions(options) & + gl_context->GetFeatures(); - SetViewPort(visibleRect.size()); - InitOpenGL(); + if (viewportControl) + gl_context->SetFeatures(gl_features); + if (options.contains("openglbicubic")) + defaultUpsize = kGLFilterBicubic; + + if ((defaultUpsize != kGLFilterBicubic) && (gl_features & kGLExtRect)) + gl_context->GetTextureType(textureType, textureRects); + + SetViewPort(display_visible_rect.size()); + + bool use_pbo = gl_features & kGLExtPBufObj; + if (osd) { - QSize osdsize = visibleRect.size(); - QSize half_size(osdsize.width() >> 1, osdsize.height() >>1); - GLuint alphatex = CreateVideoTexture(osdsize, inputTextureSize); - GLuint utex = CreateVideoTexture(half_size, inputTextureSize); - GLuint vtex = CreateVideoTexture(half_size, inputTextureSize); - GLuint ytex = CreateVideoTexture(osdsize, inputTextureSize); + QSize osdsize = display_visible_rect.size(); + GLuint tex = CreateVideoTexture(osdsize, inputTextureSize, use_pbo); - if ((alphatex && ytex && utex && vtex) && AddFilter(kGLFilterYUV2RGBA)) + if (tex && + AddFilter(kGLFilterYUV2RGBA) && + AddFilter(kGLFilterResize)) { - inputTextures.push_back(ytex); - inputTextures.push_back(utex); - inputTextures.push_back(vtex); - inputTextures.push_back(alphatex); - if (!AddFilter(kGLFilterResize)) - { - Teardown(); - return false; - } + inputTextures.push_back(tex); } + else + { + Teardown(); + } } else { - QSize half_size(videoSize.width() >> 1, videoSize.height() >>1); - GLuint utex = CreateVideoTexture(half_size, inputTextureSize); - GLuint vtex = CreateVideoTexture(half_size, inputTextureSize); - GLuint ytex = CreateVideoTexture(videoSize, inputTextureSize);; + GLuint tex = CreateVideoTexture(actual_video_dim, + inputTextureSize, use_pbo); - if ((ytex && utex && vtex) && AddFilter(kGLFilterYUV2RGB)) + if (tex && AddFilter(kGLFilterYUV2RGB)) { - inputTextures.push_back(ytex); - inputTextures.push_back(utex); - inputTextures.push_back(vtex); + inputTextures.push_back(tex); } + else + { + Teardown(); + } } if (filters.empty()) @@ -184,11 +172,12 @@ "Falling back to software conversion.\n\t\t\t" "Any opengl filters will also be disabled."); - GLuint rgb24tex = CreateVideoTexture(videoSize, inputTextureSize); + GLuint bgra32tex = CreateVideoTexture(actual_video_dim, + inputTextureSize, use_pbo); - if (rgb24tex && AddFilter(kGLFilterResize)) + if (bgra32tex && AddFilter(kGLFilterResize)) { - inputTextures.push_back(rgb24tex); + inputTextures.push_back(bgra32tex); } else { @@ -198,68 +187,81 @@ } } +#ifdef MMX + bool mmx = true; +#else + bool mmx = false; +#endif + + CheckResize(false); + + VERBOSE(VB_PLAYBACK, LOC + + QString("Using packed textures with%1 mmx and with%2 PBOs") + .arg(mmx ? "" : "out").arg(use_pbo ? "" : "out")); + return true; } -OpenGLFilterType OpenGLVideo::GetDeintFilter(void) const +void OpenGLVideo::CheckResize(bool deinterlacing) { - if (filters.count(kGLFilterKernelDeint)) - return kGLFilterKernelDeint; - if (filters.count(kGLFilterLinearBlendDeint)) - return kGLFilterLinearBlendDeint; - if (filters.count(kGLFilterOneFieldDeint)) - return kGLFilterOneFieldDeint; - if (filters.count(kGLFilterBobDeintDFR)) - return kGLFilterBobDeintDFR; - if (filters.count(kGLFilterOneFieldDeintDFR)) - return kGLFilterOneFieldDeintDFR; - if (filters.count(kGLFilterLinearBlendDeintDFR)) - return kGLFilterLinearBlendDeintDFR; - if (filters.count(kGLFilterKernelDeintDFR)) - return kGLFilterKernelDeintDFR; - if (filters.count(kGLFilterFieldOrderDFR)) - return kGLFilterFieldOrderDFR; + // to improve performance on slower cards + bool resize_up = (video_dim.height() < display_video_rect.height()) || + (video_dim.width() < display_video_rect.width()); - return kGLFilterNone; -} + // to ensure deinterlacing works correctly + bool resize_down = (video_dim.height() > display_video_rect.height()) && + deinterlacing; -bool OpenGLVideo::OptimiseFilters(void) -{ - // if video height does not match display rect height, add resize stage - // to preserve field information N.B. assumes interlaced - // if video rectangle is smaller than display rectangle, add resize stage - // to improve performance + if (resize_up && (defaultUpsize == kGLFilterBicubic)) + { + RemoveFilter(kGLFilterResize); + filters.erase(kGLFilterResize); + AddFilter(kGLFilterBicubic); + return; + } - bool needResize = ((videoSize.height() != videoRect.height()) || - (videoSize.width() < videoRect.width())); - if (needResize && !filters.count(kGLFilterResize) && - !(AddFilter(kGLFilterResize))) + if ((resize_up && (defaultUpsize == kGLFilterResize)) || resize_down) { - return false; + RemoveFilter(kGLFilterBicubic); + filters.erase(kGLFilterBicubic); + AddFilter(kGLFilterResize); + return; } + if (!filters.count(kGLFilterYUV2RGBA)) + { + RemoveFilter(kGLFilterResize); + filters.erase(kGLFilterResize); + } + + RemoveFilter(kGLFilterBicubic); + filters.erase(kGLFilterBicubic); + + OptimiseFilters(); +} + +bool OpenGLVideo::OptimiseFilters(void) +{ glfilt_map_t::reverse_iterator it; // add/remove required frame buffer objects // and link filters uint buffers_needed = 1; bool last_filter = true; - bool needtorotate = false; for (it = filters.rbegin(); it != filters.rend(); it++) { - it->second->outputBuffer = kFrameBufferObject; - it->second->rotateFrameBuffers = needtorotate; if (!last_filter) { + it->second->outputBuffer = kFrameBufferObject; uint buffers_have = it->second->frameBuffers.size(); int buffers_diff = buffers_needed - buffers_have; if (buffers_diff > 0) { uint tmp_buf, tmp_tex; - QSize fb_size = GetTextureSize(videoSize); + QSize fb_size = GetTextureSize(video_dim); for (int i = 0; i < buffers_diff; i++) { - if (!AddFrameBuffer(tmp_buf, tmp_tex, fb_size)) + if (!AddFrameBuffer(tmp_buf, fb_size, tmp_tex, video_dim)) return false; else { @@ -286,26 +288,14 @@ } else { + it->second->outputBuffer = kDefaultBuffer; last_filter = false; } - buffers_needed = it->second->numInputs; - needtorotate = (it->first == kGLFilterKernelDeint || - it->first == kGLFilterLinearBlendDeint || - it->first == kGLFilterOneFieldDeintDFR || - it->first == kGLFilterLinearBlendDeintDFR || - it->first == kGLFilterKernelDeintDFR || - it->first == kGLFilterFieldOrderDFR); - } - bool deinterlacing = hardwareDeinterlacing; - hardwareDeinterlacing = true; + SetFiltering(); - SetDeinterlacing(false); - if (deinterlacing) - SetDeinterlacing(deinterlacing); - return true; } @@ -314,131 +304,81 @@ { // filter settings included for performance only // no (obvious) quality improvement over GL_LINEAR throughout - if (filters.empty()) - return; - - if (filters.size() == 1) + if (filters.empty() || filters.size() == 1) { - SetTextureFilters(&inputTextures, GL_LINEAR); + SetTextureFilters(&inputTextures, GL_LINEAR, GL_CLAMP_TO_EDGE); return; } - SetTextureFilters(&inputTextures, GL_NEAREST); - vector textures; - glfilt_map_t::iterator it; - for (it = filters.begin(); it != filters.end(); it++) - SetTextureFilters(&(it->second->frameBufferTextures), GL_NEAREST); + SetTextureFilters(&inputTextures, GL_NEAREST, GL_CLAMP_TO_EDGE); - // resize or last active (ie don't need resize) need GL_LINEAR glfilt_map_t::reverse_iterator rit; - bool next = false; - bool resize = filters.count(kGLFilterResize); + int last_filter = 0; + for (rit = filters.rbegin(); rit != filters.rend(); rit++) { - if (next && (rit->second->outputBuffer != kNoBuffer)) + if (last_filter == 1) { - SetTextureFilters(&(rit->second->frameBufferTextures), GL_LINEAR); - return; + SetTextureFilters(&(rit->second->frameBufferTextures), + GL_LINEAR, GL_CLAMP_TO_EDGE); } - - if (resize) + else if (last_filter > 1) { - next |= ((rit->first == kGLFilterResize) || - (rit->second->outputBuffer == kDefaultBuffer)); + SetTextureFilters(&(rit->second->frameBufferTextures), + GL_NEAREST, GL_CLAMP_TO_EDGE); } } - - SetTextureFilters(&inputTextures, GL_LINEAR); } // locking ok -bool OpenGLVideo::ReInit(OpenGLContext *glcontext, bool colour_control, - bool onscreen, QSize video_size, QRect visible_rect, - QRect video_rect, QRect frame_rect, - bool viewport_control, bool osd) -{ - VERBOSE(VB_PLAYBACK, LOC + "Reinit"); - - gl_context->MakeCurrent(true); - - QString harddeint = GetDeinterlacer(); // only adds back deinterlacer - QString softdeint = softwareDeinterlacer; - bool interlacing = hardwareDeinterlacing; - bool resize = videoResize; - QRect resize_rect = videoResizeRect; - - Teardown(); - - bool success = Init(glcontext, colour_control, onscreen, video_size, - visible_rect, video_rect, frame_rect, - viewport_control, osd); - - if (harddeint != "") - success &= AddDeinterlacer(harddeint); - - softwareDeinterlacer = softdeint; - SetDeinterlacing(interlacing); - - if (resize) - SetVideoResize(resize_rect); - - return success; -} - -// locking ok bool OpenGLVideo::AddFilter(OpenGLFilterType filter) { if (filters.count(filter)) return true; + bool success = true; + VERBOSE(VB_PLAYBACK, LOC + QString("Creating %1 filter.") .arg(FilterToString(filter))); - gl_context->MakeCurrent(true); - OpenGLFilter *temp = new OpenGLFilter(); temp->numInputs = 1; + GLuint program = 0; - if ((filter == kGLFilterLinearBlendDeint) || - (filter == kGLFilterKernelDeint) || - (filter == kGLFilterFieldOrderDFR)) + if (filter == kGLFilterBicubic) { - temp->numInputs = 2; + if (helperTexture) + gl_context->DeleteTexture(helperTexture); + + helperTexture = gl_context->CreateHelperTexture(); + if (!helperTexture) + success = false; } - else if ((filter == kGLFilterYUV2RGB) || - (filter == kGLFilterOneFieldDeintDFR) || - (filter == kGLFilterKernelDeintDFR) || - (filter == kGLFilterLinearBlendDeintDFR)) - { - temp->numInputs = 3; - } - else if ((filter == kGLFilterYUV2RGBA)) - { - temp->numInputs = 4; - } - GLuint program = 0; if (filter != kGLFilterNone && filter != kGLFilterResize) { program = AddFragmentProgram(filter); if (!program) - return false; + success = false; + else + temp->fragmentPrograms.push_back(program); } - temp->fragmentProgram = program; temp->outputBuffer = kDefaultBuffer; - temp->rotateFrameBuffers = false; temp->frameBuffers.clear(); temp->frameBufferTextures.clear(); filters[filter] = temp; - if (OptimiseFilters()) + success &= OptimiseFilters(); + + if (success) return true; RemoveFilter(filter); + filters.erase(filter); return false; } @@ -449,83 +389,153 @@ if (!filters.count(filter)) return true; - VERBOSE(VB_PLAYBACK, QString("Removing %1 filter") + VERBOSE(VB_PLAYBACK, LOC + QString("Removing %1 filter") .arg(FilterToString(filter))); - gl_context->MakeCurrent(true); - - gl_context->DeleteFragmentProgram(filters[filter]->fragmentProgram); - vector temp; vector::iterator it; + temp = filters[filter]->fragmentPrograms; + for (it = temp.begin(); it != temp.end(); it++) + gl_context->DeleteFragmentProgram(*it); + filters[filter]->fragmentPrograms.clear(); + temp = filters[filter]->frameBuffers; for (it = temp.begin(); it != temp.end(); it++) gl_context->DeleteFrameBuffer(*it); + filters[filter]->frameBuffers.clear(); - temp = filters[filter]->frameBufferTextures; - for (it = temp.begin(); it != temp.end(); it++) - gl_context->DeleteTexture((*(it))); + DeleteTextures(&(filters[filter]->frameBufferTextures)); - filters.erase(filter); + delete filters[filter]; - gl_context->MakeCurrent(false); - return true; } // locking ok -bool OpenGLVideo::AddDeinterlacer(const QString &filter) +void OpenGLVideo::TearDownDeinterlacer(void) { - QString current_deinterlacer = GetDeinterlacer(); + if (!filters.count(kGLFilterYUV2RGB)) + return; - if (current_deinterlacer == filter) + OpenGLFilter *tmp = filters[kGLFilterYUV2RGB]; + + if (tmp->fragmentPrograms.size() == 3) + { + gl_context->DeleteFragmentProgram(tmp->fragmentPrograms[2]); + tmp->fragmentPrograms.pop_back(); + } + + if (tmp->fragmentPrograms.size() == 2) + { + gl_context->DeleteFragmentProgram(tmp->fragmentPrograms[1]); + tmp->fragmentPrograms.pop_back(); + } + + DeleteTextures(&referenceTextures); +} + +bool OpenGLVideo::AddDeinterlacer(const QString &deinterlacer) +{ + OpenGLContextLocker ctx_lock(gl_context); + + if (!filters.count(kGLFilterYUV2RGB)) + return false; + + if (hardwareDeinterlacer == deinterlacer) return true; - if (!current_deinterlacer.isEmpty()) - RemoveFilter(current_deinterlacer); + TearDownDeinterlacer(); - return AddFilter(filter); + bool success = true; + + uint ref_size = 2; + + if (deinterlacer == "openglbobdeint" || + deinterlacer == "openglonefield" || + deinterlacer == "opengldoubleratefieldorder") + { + ref_size = 0; + } + + if (ref_size > 0) + { + bool use_pbo = gl_features & kGLExtPBufObj; + + for (; ref_size > 0; ref_size--) + { + GLuint tex = CreateVideoTexture(actual_video_dim, inputTextureSize, use_pbo); + if (tex) + { + referenceTextures.push_back(tex); + } + else + { + success = false; + } + } + } + + uint prog1 = AddFragmentProgram(kGLFilterYUV2RGB, + deinterlacer, kScan_Interlaced); + uint prog2 = AddFragmentProgram(kGLFilterYUV2RGB, + deinterlacer, kScan_Intr2ndField); + + if (prog1 && prog2) + { + filters[kGLFilterYUV2RGB]->fragmentPrograms.push_back(prog1); + filters[kGLFilterYUV2RGB]->fragmentPrograms.push_back(prog2); + } + else + { + success = false; + } + + if (success) + { + CheckResize(hardwareDeinterlacing); + hardwareDeinterlacer = deinterlacer; + return true; + } + + hardwareDeinterlacer = ""; + TearDownDeinterlacer(); + + return false; } // locking ok -uint OpenGLVideo::AddFragmentProgram(OpenGLFilterType name) +uint OpenGLVideo::AddFragmentProgram(OpenGLFilterType name, + QString deint, FrameScanType field) { - if (!gl_context->IsFeatureSupported(kGLExtFragProg)) + if (!(gl_features & kGLExtFragProg)) { VERBOSE(VB_PLAYBACK, LOC_ERR + "Fragment programs not supported"); return 0; } - QString program = GetProgramString(name); - QString texType = (gl_context->IsFeatureSupported(kGLExtRect)) ? "RECT" : "2D"; - program.replace("%1", texType); + QString program = GetProgramString(name, deint, field); uint ret; if (gl_context->CreateFragmentProgram(program, ret)) - { - VERBOSE(VB_PLAYBACK, LOC + QString("Created fragment program %1.") - .arg(FilterToString(name))); - return ret; - } return 0; } // locking ok -bool OpenGLVideo::AddFrameBuffer(uint &framebuffer, - uint &texture, QSize size) +bool OpenGLVideo::AddFrameBuffer(uint &framebuffer, QSize fb_size, + uint &texture, QSize vid_size) { - if (!gl_context->IsFeatureSupported(kGLExtFBufObj)) + if (!(gl_features & kGLExtFBufObj)) { VERBOSE(VB_PLAYBACK, LOC_ERR + "Framebuffer binding not supported."); return false; } - texture = gl_context->CreateTexture(); + texture = gl_context->CreateTexture(fb_size, vid_size, false, textureType); - bool ok = gl_context->CreateFrameBuffer(framebuffer, texture, size); + bool ok = gl_context->CreateFrameBuffer(framebuffer, texture); if (!ok) gl_context->DeleteTexture(texture); @@ -536,8 +546,8 @@ // locking ok void OpenGLVideo::SetViewPort(const QSize &viewPortSize) { - uint w = max(viewPortSize.width(), videoSize.width()); - uint h = max(viewPortSize.height(), videoSize.height()); + uint w = max(viewPortSize.width(), video_dim.width()); + uint h = max(viewPortSize.height(), video_dim.height()); viewportSize = QSize(w, h); @@ -546,60 +556,26 @@ VERBOSE(VB_PLAYBACK, LOC + QString("Viewport: %1x%2") .arg(w).arg(h)); - - SetViewPortPrivate(viewportSize); + gl_context->SetViewPort(viewportSize); } -void OpenGLVideo::SetViewPortPrivate(const QSize &viewPortSize) -{ - glViewport(0, 0, viewPortSize.width(), viewPortSize.height()); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, viewPortSize.width() - 1, - 0, viewPortSize.height() - 1, 1, -1); // aargh... - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -} - // locking ok -void OpenGLVideo::InitOpenGL(void) +uint OpenGLVideo::CreateVideoTexture(QSize size, QSize &tex_size, + bool use_pbo) { - gl_context->MakeCurrent(true); - glDisable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // for gl osd - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glDisable(GL_CULL_FACE); - gl_context->EnableTextures();; - glShadeModel(GL_FLAT); - glDisable(GL_POLYGON_SMOOTH); - glDisable(GL_LINE_SMOOTH); - glDisable(GL_POINT_SMOOTH); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - glFlush(); - gl_context->MakeCurrent(false); -} - -// locking ok -uint OpenGLVideo::CreateVideoTexture(QSize size, QSize &tex_size) -{ - uint tmp_tex = gl_context->CreateTexture(); - QSize temp = GetTextureSize(size); + uint tmp_tex = gl_context->CreateTexture(temp, size, use_pbo, + textureType); - if ((temp.width() > (int)gl_context->GetMaxTexSize()) || - (temp.height() > (int)gl_context->GetMaxTexSize()) || - !gl_context->SetupTexture(temp, tmp_tex, GL_LINEAR)) + if (!tmp_tex) { VERBOSE(VB_PLAYBACK, LOC_ERR + "Could not create texture."); - gl_context->DeleteTexture(tmp_tex); return 0; } tex_size = temp; - VERBOSE(VB_PLAYBACK, LOC + QString("Created main input texture %1x%2") + VERBOSE(VB_PLAYBACK, LOC + QString("Created texture (%1x%2)") .arg(temp.width()).arg(temp.height())); return tmp_tex; @@ -608,7 +584,7 @@ // locking ok QSize OpenGLVideo::GetTextureSize(const QSize &size) { - if (gl_context->IsFeatureSupported(kGLExtRect)) + if (textureRects) return size; int w = 64; @@ -628,10 +604,12 @@ } // locking ok -void OpenGLVideo::UpdateInputFrame(const VideoFrame *frame) +void OpenGLVideo::UpdateInputFrame(const VideoFrame *frame, bool soft_bob) { - if (frame->width != videoSize.width() || - frame->height != videoSize.height() || + OpenGLContextLocker ctx_lock(gl_context); + + if (frame->width != actual_video_dim.width() || + frame->height != actual_video_dim.height() || frame->width < 1 || frame->height < 1) { @@ -641,64 +619,65 @@ if (filters.count(kGLFilterYUV2RGB) && (frame->codec == FMT_YV12)) { - UpdateInput(frame->buf, frame->offsets, 0, FMT_YV12, videoSize); + if (hardwareDeinterlacing) + RotateTextures(); + + gl_context->UpdateTexture(inputTextures[0], frame->buf, + frame->offsets, frame->pitches, FMT_YV12, + frame->interlaced_frame && !soft_bob); + inputUpdated = true; return; } // software yuv2rgb - if (convertSize != videoSize) + if (convertSize != actual_video_dim) { ShutDownYUV2RGB(); VERBOSE(VB_PLAYBACK, LOC + "Init software conversion."); - convertSize = videoSize; + convertSize = actual_video_dim; convertBuf = new unsigned char[ - (videoSize.width() * videoSize.height() * 3) + 128]; + (actual_video_dim.width() * actual_video_dim.height() * 4) + 128]; } if (convertBuf) { AVPicture img_in, img_out; - avpicture_fill(&img_out, (uint8_t *)convertBuf, PIX_FMT_RGB24, + avpicture_fill(&img_out, (uint8_t *)convertBuf, PIX_FMT_BGRA, convertSize.width(), convertSize.height()); avpicture_fill(&img_in, (uint8_t *)frame->buf, PIX_FMT_YUV420P, convertSize.width(), convertSize.height()); - img_convert(&img_out, PIX_FMT_RGB24, + img_convert(&img_out, PIX_FMT_BGRA, &img_in, PIX_FMT_YUV420P, convertSize.width(), convertSize.height()); int offset = 0; - UpdateInput(convertBuf, &offset, 0, FMT_RGB24, convertSize); + gl_context->UpdateTexture(inputTextures[0], convertBuf, + &offset, &offset, FMT_BGRA); } + + inputUpdated = true; } // locking ok void OpenGLVideo::UpdateInput(const unsigned char *buf, const int *offsets, - uint texture_index, int format, QSize size) + int format, QSize size, + const unsigned char *alpha) { - inputUpdated = false; + OpenGLContextLocker ctx_lock(gl_context); - if (texture_index >= inputTextures.size()) + if (size.width() != actual_video_dim.width() || + size.height() != actual_video_dim.height() || + format != FMT_YV12 || !alpha) return; - copy_pixels_to_texture( - buf + offsets[0], format, size, - inputTextures[texture_index], gl_context->GetTextureType()); + int pitches[3] = {size.width(), size.width() >> 1, size.width() >> 1}; - if (FMT_YV12 == format) - { - QSize chroma_size(size.width() >> 1, size.height() >> 1); - copy_pixels_to_texture( - buf + offsets[1], format, chroma_size, - inputTextures[texture_index + 1], - gl_context->GetTextureType()); - copy_pixels_to_texture( - buf + offsets[2], format, chroma_size, - inputTextures[texture_index + 2], - gl_context->GetTextureType()); - } + gl_context->UpdateTexture(inputTextures[0], buf, + offsets, pitches, FMT_YV12, + false, alpha); inputUpdated = true; } @@ -718,14 +697,16 @@ // TODO shouldn't this take a QSize, not QRect? void OpenGLVideo::SetVideoResize(const QRect &rect) { - bool abort = ((rect.right() > videoSize.width()) || - (rect.bottom() > videoSize.height()) || - (rect.width() > videoSize.width()) || - (rect.height() > videoSize.height())); + OpenGLContextLocker ctx_lock(gl_context); + bool abort = ((rect.right() > video_dim.width()) || + (rect.bottom() > video_dim.height()) || + (rect.width() > video_dim.width()) || + (rect.height() > video_dim.height())); + // if resize == existing frame, no need to carry on - abort |= !rect.left() && !rect.top() && (rect.size() == videoSize); + abort |= !rect.left() && !rect.top() && (rect.size() == video_dim); if (!abort) { @@ -740,6 +721,8 @@ // locking ok void OpenGLVideo::DisableVideoResize(void) { + OpenGLContextLocker ctx_lock(gl_context); + videoResize = false; videoResizeRect = QRect(0, 0, 0, 0); } @@ -749,19 +732,19 @@ { // FIXME video aspect == display aspect - if ((videoSize.height() <= 0) || (videoSize.width() <= 0)) + if ((video_dim.height() <= 0) || (video_dim.width() <= 0)) return; - float height = visibleRect.height(); + float height = display_visible_rect.height(); float new_top = height - ((float)videoResizeRect.bottom() / - (float)videoSize.height()) * height; + (float)video_dim.height()) * height; float new_bottom = height - ((float)videoResizeRect.top() / - (float)videoSize.height()) * height; + (float)video_dim.height()) * height; - left = (((float) videoResizeRect.left() / (float) videoSize.width()) * - visibleRect.width()); - right = (((float) videoResizeRect.right() / (float) videoSize.width()) * - visibleRect.width()); + left = (((float) videoResizeRect.left() / (float) video_dim.width()) * + display_visible_rect.width()); + right = (((float) videoResizeRect.right() / (float) video_dim.width()) * + display_visible_rect.width()); top = new_top; bottom = new_bottom; @@ -773,37 +756,10 @@ if (deinterlacing == hardwareDeinterlacing) return; - VERBOSE(VB_PLAYBACK, LOC + QString("Turning %1 deinterlacing.") - .arg(deinterlacing ? "on" : "off")); - hardwareDeinterlacing = deinterlacing; - glfilt_map_t::iterator it = filters.begin(); - for (; it != filters.end(); it++) - { - it->second->outputBuffer = kFrameBufferObject; - - if ((it->first >= kGLFilterLinearBlendDeint) && - (it->first <= kGLFilterOneFieldDeintDFR) && - !deinterlacing) - { - it->second->outputBuffer = kNoBuffer; - } - } - - glfilt_map_t::reverse_iterator rit = filters.rbegin(); - for (; rit != filters.rend(); rit++) - { - if (rit->second->outputBuffer == kFrameBufferObject) - { - rit->second->outputBuffer = kDefaultBuffer; - break; - } - } - - gl_context->MakeCurrent(true); - SetFiltering(); - gl_context->MakeCurrent(false); + OpenGLContextLocker ctx_lock(gl_context); + CheckResize(hardwareDeinterlacing); } // locking ok @@ -813,24 +769,19 @@ if (inputTextures.empty() || filters.empty()) return; + OpenGLContextLocker ctx_lock(gl_context); + + // enable correct texture type + gl_context->EnableTextures(inputTextures[0]); + vector inputs = inputTextures; QSize inputsize = inputTextureSize; + QSize realsize = GetTextureSize(video_dim); uint numfilters = filters.size(); glfilt_map_t::iterator it; for (it = filters.begin(); it != filters.end(); it++) { - if (it->second->rotateFrameBuffers && - !(it->first == kGLFilterYUV2RGB && scan == kScan_Intr2ndField)) - { - Rotate(&(it->second->frameBufferTextures)); - Rotate(&(it->second->frameBuffers)); - } - - // skip disabled filters - if (it->second->outputBuffer == kNoBuffer) - continue; - OpenGLFilterType type = it->first; OpenGLFilter *filter = it->second; @@ -838,37 +789,27 @@ if (!inputUpdated && type == kGLFilterYUV2RGBA) { inputs = filter->frameBufferTextures; - inputsize = videoSize; + inputsize = realsize; continue; } - // skip colour conversion for frames already in frame buffer - if (!inputUpdated && (frame == currentFrameNum) && - (type == kGLFilterYUV2RGB) && (frame != 0) && - (!(softwareDeinterlacing && softwareDeinterlacer == "bobdeint"))) - { - inputs = filter->frameBufferTextures; - inputsize = videoSize; - continue; - } - // texture coordinates - float t_right = (float)videoSize.width(); - float t_bottom = (float)videoSize.height(); + float t_right = (float)video_dim.width(); + float t_bottom = (float)video_dim.height(); float t_top = 0.0f; float t_left = 0.0f; - float trueheight = (float)videoSize.height(); + float trueheight = (float)video_dim.height(); // only apply overscan on last filter if (filter->outputBuffer == kDefaultBuffer) { - t_left = (float)frameRect.left(); - t_right = (float)frameRect.width() + t_left; - t_top = (float)frameRect.top(); - t_bottom = (float)frameRect.height() + t_top; + t_left = (float)video_rect.left(); + t_right = (float)video_rect.width() + t_left; + t_top = (float)video_rect.top(); + t_bottom = (float)video_rect.height() + t_top; } - if (!gl_context->IsFeatureSupported(kGLExtRect) && + if (!textureRects && (inputsize.width() > 0) && (inputsize.height() > 0)) { t_right /= inputsize.width(); @@ -878,30 +819,14 @@ trueheight /= inputsize.height(); } - float line_height = (trueheight / (float)videoSize.height()); - float bob = line_height / 2.0f; - - if (type == kGLFilterBobDeintDFR) + // software bobdeint + if ((softwareDeinterlacer == "bobdeint") && + softwareDeinterlacing && + (filter->outputBuffer == kDefaultBuffer)) { + float bob = (trueheight / (float)video_dim.height()) / 4.0f; if (scan == kScan_Interlaced) { - t_bottom += bob; - t_top += bob; - } - if (scan == kScan_Intr2ndField) - { - t_bottom -= bob; - t_top -= bob; - } - } - - if (softwareDeinterlacer == "bobdeint" && - softwareDeinterlacing && (type == kGLFilterYUV2RGB || - (type == kGLFilterResize && numfilters == 1))) - { - bob = line_height / 4.0f; - if (scan == kScan_Interlaced) - { t_top /= 2; t_bottom /= 2; t_bottom += bob; @@ -916,36 +841,42 @@ } } - float t_right_uv = t_right; - float t_top_uv = t_top; - float t_bottom_uv = t_bottom; - float t_left_uv = t_left; - - if (gl_context->IsFeatureSupported(kGLExtRect)) - { - t_right_uv /= 2; - t_top_uv /= 2; - t_bottom_uv /= 2; - t_left_uv /= 2; - } - // vertex coordinates - QRect display = (filter->frameBuffers.empty() || - filter->outputBuffer == kDefaultBuffer) ? - videoRect : frameBufferRect; + QRect display = (filter->frameBuffers.empty() || + filter->outputBuffer == kDefaultBuffer) ? + display_video_rect : frameBufferRect; float vleft = display.left(); float vright = display.right(); float vtop = display.top(); float vbot = display.bottom(); + // hardware bobdeint + if (filter->outputBuffer == kDefaultBuffer && + hardwareDeinterlacing && + hardwareDeinterlacer == "openglbobdeint") + { + float bob = ((float)display.height() / (float)video_dim.height()) + / 2.0f; + if (scan == kScan_Interlaced) + { + vbot -= bob; + vtop -= bob; + } + if (scan == kScan_Intr2ndField) + { + vbot += bob; + vtop += bob; + } + } + // resize for interactive tv if (videoResize && filter->outputBuffer == kDefaultBuffer) CalculateResize(vleft, vtop, vright, vbot); - if (invertVideo && - ((type == kGLFilterYUV2RGB) || (type == kGLFilterYUV2RGBA)) || - ((type == kGLFilterResize) && (numfilters == 1))) + // invert horizontally + if (((type == kGLFilterYUV2RGB) || (type == kGLFilterYUV2RGBA)) || + (filter->outputBuffer == kDefaultBuffer && numfilters == 1)) { float temp = vtop; vtop = vbot; @@ -956,18 +887,15 @@ switch (filter->outputBuffer) { case kDefaultBuffer: - if (frameBuffer) - gl_context->BindFramebuffer(frameBuffer); - // clear the buffer if (viewportControl) { glClear(GL_COLOR_BUFFER_BIT); - SetViewPortPrivate(visibleRect.size()); + gl_context->SetViewPort(display_visible_rect.size()); } else { - SetViewPortPrivate(masterViewportSize); + gl_context->SetViewPort(masterViewportSize); } break; @@ -976,58 +904,69 @@ if (!filter->frameBuffers.empty()) { gl_context->BindFramebuffer(filter->frameBuffers[0]); - SetViewPortPrivate(frameBufferRect.size()); + gl_context->SetViewPort(frameBufferRect.size()); } break; - case kNoBuffer: + default: continue; } // bind correct textures - for (uint i = 0; i < inputs.size(); i++) + uint active_tex = 0; + for (; active_tex < inputs.size(); active_tex++) { - glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(gl_context->GetTextureType(), inputs[i]); + glActiveTexture(GL_TEXTURE0 + active_tex); + glBindTexture(textureType, inputs[active_tex]); } + if (!referenceTextures.empty() && + hardwareDeinterlacing && + type == kGLFilterYUV2RGB) + { + uint max = inputs.size() + referenceTextures.size(); + uint ref = 0; + for (; active_tex < max; active_tex++, ref++) + { + glActiveTexture(GL_TEXTURE0 + active_tex); + glBindTexture(textureType, referenceTextures[ref]); + } + } + + if (helperTexture && type == kGLFilterBicubic) + { + glActiveTexture(GL_TEXTURE0 + active_tex); + glBindTexture(GL_TEXTURE_1D/*N.B.*/, helperTexture); + } + // enable fragment program and set any environment variables if ((type != kGLFilterNone) && (type != kGLFilterResize)) { glEnable(GL_FRAGMENT_PROGRAM_ARB); - gl_context->BindFragmentProgram(filter->fragmentProgram); - float field = -line_height; + GLuint program = 0; + if (type == kGLFilterYUV2RGB) + { + if (hardwareDeinterlacing && + filter->fragmentPrograms.size() == 3) + { + if (scan == kScan_Interlaced) + program = 1; + else if (scan == kScan_Intr2ndField) + program = 2; + } + } + + gl_context->BindFragmentProgram(filter->fragmentPrograms[program]); + switch (type) { case kGLFilterYUV2RGB: case kGLFilterYUV2RGBA: if (useColourControl) - { - gl_context->InitFragmentParams( - 0, - pictureAttribs[kPictureAttribute_Brightness], - pictureAttribs[kPictureAttribute_Contrast], - pictureAttribs[kPictureAttribute_Colour], - 0.0f); - } + gl_context->SetColourParams(); break; - case kGLFilterBobDeintDFR: - case kGLFilterOneFieldDeintDFR: - case kGLFilterKernelDeintDFR: - case kGLFilterFieldOrderDFR: - case kGLFilterLinearBlendDeintDFR: - if (scan == kScan_Intr2ndField) - field *= -1; - - case kGLFilterOneFieldDeint: - case kGLFilterKernelDeint: - case kGLFilterLinearBlendDeint: - gl_context->InitFragmentParams( - 0, line_height * 2.0f, field, 0.0f, 0.0f); - break; - case kGLFilterNone: case kGLFilterResize: break; @@ -1041,43 +980,15 @@ // draw quad glBegin(GL_QUADS); glTexCoord2f(t_left, t_top); - if (type == kGLFilterYUV2RGB || type == kGLFilterYUV2RGBA) - { - glMultiTexCoord2f(GL_TEXTURE1, t_left_uv, t_top_uv); - glMultiTexCoord2f(GL_TEXTURE2, t_left_uv, t_top_uv); - if (type == kGLFilterYUV2RGBA) - glMultiTexCoord2f(GL_TEXTURE3, t_left_uv, t_top_uv); - } glVertex2f(vleft, vtop); glTexCoord2f(t_right, t_top); - if (type == kGLFilterYUV2RGB || type == kGLFilterYUV2RGBA) - { - glMultiTexCoord2f(GL_TEXTURE1, t_right_uv, t_top_uv); - glMultiTexCoord2f(GL_TEXTURE2, t_right_uv, t_top_uv); - if (type == kGLFilterYUV2RGBA) - glMultiTexCoord2f(GL_TEXTURE3, t_right, t_top); - } glVertex2f(vright, vtop); glTexCoord2f(t_right, t_bottom); - if (type == kGLFilterYUV2RGB || type == kGLFilterYUV2RGBA) - { - glMultiTexCoord2f(GL_TEXTURE1, t_right_uv, t_bottom_uv); - glMultiTexCoord2f(GL_TEXTURE2, t_right_uv, t_bottom_uv); - if (type == kGLFilterYUV2RGBA) - glMultiTexCoord2f(GL_TEXTURE3, t_right, t_bottom); - } glVertex2f(vright, vbot); glTexCoord2f(t_left, t_bottom); - if (type == kGLFilterYUV2RGB || type == kGLFilterYUV2RGBA) - { - glMultiTexCoord2f(GL_TEXTURE1, t_left_uv, t_bottom_uv); - glMultiTexCoord2f(GL_TEXTURE2, t_left_uv, t_bottom_uv); - if (type == kGLFilterYUV2RGBA) - glMultiTexCoord2f(GL_TEXTURE3, t_left_uv, t_bottom); - } glVertex2f(vleft, vbot); glEnd(); @@ -1093,76 +1004,50 @@ } // switch back to default framebuffer - if (filter->outputBuffer != kDefaultBuffer || frameBuffer) + if (filter->outputBuffer != kDefaultBuffer) gl_context->BindFramebuffer(0); inputs = filter->frameBufferTextures; - inputsize = videoSize; + inputsize = realsize; } currentFrameNum = frame; inputUpdated = false; } -void OpenGLVideo::Rotate(vector *target) +void OpenGLVideo::RotateTextures(void) { - if (target->size() < 2) + if (referenceTextures.size() < 2) return; - GLuint tmp = (*target)[target->size() - 1]; - for (uint i = target->size() - 1; i > 0; i--) - (*target)[i] = (*target)[i - 1]; + GLuint tmp = referenceTextures[referenceTextures.size() - 1]; - (*target)[0] = tmp; + for (uint i = referenceTextures.size() - 1; i > 0; i--) + referenceTextures[i] = referenceTextures[i - 1]; + + referenceTextures[0] = inputTextures[0]; + inputTextures[0] = tmp; } -// locking ok -int OpenGLVideo::SetPictureAttribute( - PictureAttribute attribute, int newValue) +void OpenGLVideo::DeleteTextures(vector *textures) { - if (!useColourControl) - return -1; + if ((*textures).empty()) + return; - int ret = -1; - switch (attribute) - { - case kPictureAttribute_Brightness: - ret = newValue; - pictureAttribs[attribute] = (newValue * 0.02f) - 0.5f; - break; - case kPictureAttribute_Contrast: - case kPictureAttribute_Colour: - ret = newValue; - pictureAttribs[attribute] = (newValue * 0.02f); - break; - case kPictureAttribute_Hue: // not supported yet... - break; - default: - break; - } - - return ret; + for (uint i = 0; i < (*textures).size(); i++) + gl_context->DeleteTexture((*textures)[i]); + (*textures).clear(); } -PictureAttributeSupported -OpenGLVideo::GetSupportedPictureAttributes(void) const -{ - return (!useColourControl) ? - kPictureAttributeSupported_None : - (PictureAttributeSupported) - (kPictureAttributeSupported_Brightness | - kPictureAttributeSupported_Contrast | - kPictureAttributeSupported_Colour); -} - // locking ok -void OpenGLVideo::SetTextureFilters(vector *textures, int filt) +void OpenGLVideo::SetTextureFilters(vector *textures, + int filt, int wrap) { if (textures->empty()) return; for (uint i = 0; i < textures->size(); i++) - gl_context->SetupTextureFilters((*textures)[i], filt); + gl_context->SetTextureFilters((*textures)[i], filt, wrap); } // locking ok @@ -1174,24 +1059,10 @@ ret = kGLFilterYUV2RGB; else if (filter.contains("osd")) ret = kGLFilterYUV2RGBA; - else if (filter.contains("openglkerneldeint")) - ret = kGLFilterKernelDeint; - else if (filter.contains("opengllinearblend")) - ret = kGLFilterLinearBlendDeint; - else if (filter.contains("openglonefield")) - ret = kGLFilterOneFieldDeint; - else if (filter.contains("openglbobdeint")) - ret = kGLFilterBobDeintDFR; - else if (filter.contains("opengldoubleratelinearblend")) - ret = kGLFilterLinearBlendDeintDFR; - else if (filter.contains("opengldoubleratekerneldeint")) - ret = kGLFilterKernelDeintDFR; - else if (filter.contains("opengldoublerateonefield")) - ret = kGLFilterOneFieldDeintDFR; - else if (filter.contains("opengldoubleratefieldorder")) - ret = kGLFilterFieldOrderDFR; else if (filter.contains("resize")) ret = kGLFilterResize; + else if (filter.contains("bicubic")) + ret = kGLFilterBicubic; return ret; } @@ -1207,294 +1078,552 @@ return "master"; case kGLFilterYUV2RGBA: return "osd"; - case kGLFilterKernelDeint: - return "openglkerneldeint"; - case kGLFilterLinearBlendDeint: - return "opengllinearblend"; - case kGLFilterOneFieldDeint: - return "openglonefield"; - case kGLFilterBobDeintDFR: - return "openglbobdeint"; - case kGLFilterLinearBlendDeintDFR: - return "opengldoubleratelinearblend"; - case kGLFilterKernelDeintDFR: - return "opengldoubleratekerneldeint"; - case kGLFilterOneFieldDeintDFR: - return "opengldoublerateonefield"; - case kGLFilterFieldOrderDFR: - return "opengldoubleratefieldorder"; case kGLFilterResize: return "resize"; + case kGLFilterBicubic: + return "bicubic"; } return ""; } -static const QString yuv2rgb1a = -"ATTRIB ytex = fragment.texcoord[0];" -"ATTRIB uvtex = fragment.texcoord[1];" -"TEMP res, tmp;"; +static const QString attrib_fast = +"ATTRIB tex = fragment.texcoord[0];\n"; -static const QString yuv2rgb1b = -"TEMP alpha;" -"TEX alpha, ytex, texture[3], %1;"; +static const QString var_alpha = +"TEMP alpha;\n"; -static const QString yuv2rgb1c = -"TEX res, ytex, texture[0], %1;" -"TEX tmp.x, uvtex, texture[1], %1;" -"TEX tmp.y, uvtex, texture[2], %1;"; +static const QString tex_alpha = +"TEX alpha, tex, texture[3], %1;\n"; -static const QString yuv2rgb2 = -"PARAM adj = program.env[0];" -"SUB res, res, 0.5;" -"MAD res, res, adj.yyyy, adj.xxxx;" -"SUB tmp, tmp, { 0.5, 0.5 };" -"MAD tmp, adj.zzzz, tmp, 0.5;"; +static const QString tex_fast = +"TEX res, tex, texture[0], %1;\n"; -static const QString yuv2rgb3 = -"MAD res, res, 1.164, -0.063;" -"SUB tmp, tmp, { 0.5, 0.5 };" -"MAD res, { 0, -.392, 2.017 }, tmp.xxxw, res;"; +static const QString param_colour = +"PARAM adj = program.env[0];\n"; -static const QString yuv2rgb4 = -"MAD result.color, { 1.596, -.813, 0, 0 }, tmp.yyyw, res;"; +static const QString calc_colour_fast = +"SUB res, res, 0.5;\n" +"MAD res, res, adj.zzzy, adj.wwwx;\n"; -static const QString yuv2rgb5 = -"MAD result.color, { 0, -.813, 1.596, 0 }, tmp.yyyw, res.bgra;"; +static const QString end_alpha = +"MOV result.color.a, alpha.a;\n"; -static const QString yuv2rgb6 = -"MOV result.color.a, alpha.a;"; +static const QString var_fast = +"TEMP tmp, res;\n"; -// locking ok -QString OpenGLVideo::GetProgramString(OpenGLFilterType name) +static const QString calc_fast_alpha = +"MOV result.color.a, res.g;\n"; + +static const QString end_fast = +"SUB tmp, res.rbgg, { 0.5, 0.5 };\n" +"MAD res, res.a, 1.164, -0.063;\n" +"MAD res, { 0, -.392, 2.017 }, tmp.xxxw, res;\n" +"MAD result.color, { 1.596, -.813, 0, 0 }, tmp.yyyw, res;\n"; + +static const QString end_fast_alpha = +"SUB tmp, res.rbgg, { 0.5, 0.5 };\n" +"MAD res, res.a, 1.164, -0.063;\n" +"MAD res, { 0, -.392, 2.017 }, tmp.xxxw, res;\n" +"MAD result.color.rgb, { 1.596, -.813, 0, 0 }, tmp.yyyw, res;\n"; + +static const QString var_deint = +"TEMP other, current, mov, prev;\n"; + +static const QString field_calc = +"MUL prev, tex.yyyy, %2;\n" +"FRC prev, prev;\n" +"SUB prev, prev, 0.5;\n"; + +static const QString bobdeint[2] = { +field_calc + +"ADD other, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX other, other, texture[0], %1;\n" +"CMP res, prev, res, other;\n", +field_calc + +"SUB other, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX other, other, texture[0], %1;\n" +"CMP res, prev, other, res;\n" +}; + +static const QString deint_end_top = +"CMP other, mov, current, other;\n" +"CMP res, prev, current, other;\n"; + +static const QString deint_end_bot = +"CMP other, mov, current, other;\n" +"CMP res, prev, other, current;\n"; + +static const QString motion_calc = +"ABS mov, mov;\n" +"SUB mov, mov, 0.07;\n"; + +static const QString motion_top = +"SUB mov, prev, current;\n" + motion_calc; + +static const QString motion_bot = +"SUB mov, res, current;\n" + motion_calc; + +static const QString doublerateonefield[2] = { +"TEX current, tex, texture[1], %1;\n" +"TEX prev, tex, texture[2], %1;\n" +"ADD other, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX other, other, texture[1], %1;\n" ++ motion_top + field_calc + deint_end_top, + +"TEX current, tex, texture[1], %1;\n" +"SUB other, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX other, other, texture[1], %1;\n" ++ motion_bot + field_calc + deint_end_bot +}; + +static const QString linearblend[2] = { +"TEX current, tex, texture[1], %1;\n" +"TEX prev, tex, texture[2], %1;\n" +"ADD other, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX other, other, texture[1], %1;\n" +"SUB mov, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX mov, mov, texture[1], %1;\n" +"LRP other, 0.5, other, mov;\n" ++ motion_top + field_calc + deint_end_top, + +"TEX current, tex, texture[1], %1;\n" +"SUB other, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX other, other, texture[1], %1;\n" +"ADD mov, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX mov, mov, texture[1], %1;\n" +"LRP other, 0.5, other, mov;\n" ++ motion_bot + field_calc + deint_end_bot +}; + +static const QString kerneldeint[2] = { +"TEX current, tex, texture[1], %1;\n" +"TEX prev, tex, texture[2], %1;\n" ++ motion_top + +"MUL other, 0.125, prev;\n" +"MAD other, 0.125, current, other;\n" +"ADD prev, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX prev, prev, texture[1], %1;\n" +"MAD other, 0.5, prev, other;\n" +"SUB prev, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX prev, prev, texture[1], %1;\n" +"MAD other, 0.5, prev, other;\n" +"ADD prev, tex, {0.0, %4, 0.0, 0.0};\n" +"TEX mov, prev, texture[1], %1;\n" +"MAD other, -0.0625, mov, other;\n" +"TEX mov, prev, texture[2], %1;\n" +"MAD other, -0.0625, mov, other;\n" +"SUB prev, tex, {0.0, %4, 0.0, 0.0};\n" +"TEX mov, prev, texture[1], %1;\n" +"MAD other, -0.0625, mov, other;\n" +"TEX mov, prev, texture[2], %1;\n" +"MAD other, -0.0625, mov, other;\n" ++ field_calc + deint_end_top, + +"TEX current, tex, texture[1], %1;\n" ++ motion_bot + +"MUL other, 0.125, res;\n" +"MAD other, 0.125, current, other;\n" +"ADD prev, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX prev, prev, texture[1], %1;\n" +"MAD other, 0.5, prev, other;\n" +"SUB prev, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX prev, prev, texture[1], %1;\n" +"MAD other, 0.5, prev, other;\n" +"ADD prev, tex, {0.0, %4, 0.0, 0.0};\n" +"TEX mov, prev, texture[1], %1;\n" +"MAD other, -0.0625, mov, other;\n" +"TEX mov, prev, texture[0], %1;\n" +"MAD other, -0.0625, mov, other;\n" +"SUB prev, tex, {0.0, %4, 0.0, 0.0};\n" +"TEX mov, prev, texture[1], %1;\n" +"MAD other, -0.0625, mov, other;\n" +"TEX mov, prev, texture[0], %1;\n" +"MAD other, -0.0625, mov, other;\n" ++ field_calc + deint_end_bot +}; + +static const QString yadif_setup = +"TEMP a,b,c,e,f,g,h,j,k,l;\n" +"TEMP a1,b1,f1,g1,h1,i1,j1,l1,m1,n1;\n" +"ALIAS d1 = f;\n" +"ALIAS k1 = g;\n" +"ALIAS c1 = prev;\n" +"ALIAS e1 = mov;\n" +"ALIAS p0 = res;\n" +"ALIAS p1 = c;\n" +"ALIAS p3 = h;\n" +"ALIAS spred1 = a;\n" +"ALIAS spred2 = b;\n" +"ALIAS spred3 = c;\n" +"ALIAS spred4 = e;\n" +"ALIAS spred5 = f;\n" +"ALIAS sscore = g;\n" +"ALIAS score1 = h;\n" +"ALIAS score2 = j;\n" +"ALIAS score3 = k;\n" +"ALIAS score4 = l;\n" +"ALIAS if1 = a1;\n" +"ALIAS if2 = b1;\n" +"TEMP p2, p4;\n" +"ALIAS diff1 = a;\n" +"ALIAS diff2 = b;\n" +"TEMP diff0;\n"; + +static const QString yadif_spatial_sample = +"ADD tmp, tex, {%5, %3, 0.0, 0.0};\n" +"TEX e1, tmp, texture[1], %1;\n" +"ADD tmp, tmp, {%5, 0.0, 0.0, 0.0};\n" +"TEX f1, tmp, texture[1], %1;\n" +"ADD tmp, tmp, {%5, 0.0, 0.0, 0.0};\n" +"TEX g1, tmp, texture[1], %1;\n" +"SUB tmp, tmp, {0.0, %4, 0.0, 0.0};\n" +"TEX n1, tmp, texture[1], %1;\n" +"SUB tmp, tmp, {%5, 0.0, 0.0, 0.0};\n" +"TEX m1, tmp, texture[1], %1;\n" +"SUB tmp, tmp, {%5, 0.0, 0.0, 0.0};\n" +"TEX l1, tmp, texture[1], %1;\n" + +"SUB tmp, tex, {%5, %3, 0.0, 0.0};\n" +"TEX j1, tmp, texture[1], %1;\n" +"SUB tmp, tmp, {%5, 0.0, 0.0, 0.0};\n" +"TEX i1, tmp, texture[1], %1;\n" +"SUB tmp, tmp, {%5, 0.0, 0.0, 0.0};\n" +"TEX h1, tmp, texture[1], %1;\n" +"ADD tmp, tmp, {0.0, %4, 0.0, 0.0};\n" +"TEX a1, tmp, texture[1], %1;\n" +"ADD tmp, tmp, {%5, 0.0, 0.0, 0.0};\n" +"TEX b1, tmp, texture[1], %1;\n" +"ADD tmp, tmp, {%5, 0.0, 0.0, 0.0};\n" +"TEX c1, tmp, texture[1], %1;\n"; + +static const QString yadif_calc = +"LRP p0, 0.5, c, h;\n" +"MOV p1, f;\n" +"LRP p2, 0.5, d, i;\n" +"MOV p3, g;\n" +"LRP p4, 0.5, e, j;\n" + +"SUB diff0, d, i;\n" +"ABS diff0, diff0;\n" +"SUB tmp, a, f;\n" +"ABS tmp, tmp;\n" +"SUB diff1, b, g;\n" +"ABS diff1, diff1;\n" +"LRP diff1, 0.5, diff1, tmp;\n" +"SUB tmp, k, f;\n" +"ABS tmp, tmp;\n" +"SUB diff2, g, l;\n" +"ABS diff2, diff2;\n" +"LRP diff2, 0.5, diff2, tmp;\n" +"MAX diff0, diff0, diff1;\n" +"MAX diff0, diff0, diff2;\n" + +// mode < 2 +"SUB tmp, p0, p1;\n" +"SUB other, p4, p3;\n" +"MIN spred1, tmp, other;\n" +"MAX spred2, tmp, other;\n" +"SUB tmp, p2, p1;\n" +"SUB other, p2, p3;\n" +"MAX spred1, spred1, tmp;\n" +"MAX spred1, spred1, other;\n" +"MIN spred2, spred2, tmp;\n" +"MIN spred2, spred2, other;\n" +"MAX spred1, spred2, -spred1;\n" +"MAX diff0, diff0, spred1;\n" + +// spatial prediction +"LRP spred1, 0.5, d1, k1;\n" +"LRP spred2, 0.5, c1, l1;\n" +"LRP spred3, 0.5, b1, m1;\n" +"LRP spred4, 0.5, e1, j1;\n" +"LRP spred5, 0.5, f1, i1;\n" + +"SUB sscore, c1, j1;\n" +"ABS sscore, sscore;\n" +"SUB tmp, d1, k1;\n" +"ABS tmp, tmp;\n" +"ADD sscore, sscore, tmp;\n" +"SUB tmp, e1, l1;\n" +"ABS tmp, tmp;\n" +"ADD sscore, sscore, tmp;\n" +"SUB sscore, sscore, 1.0;\n" + +"SUB score1, b1, k1;\n" +"ABS score1, score1;\n" +"SUB tmp, c1, l1;\n" +"ABS tmp, tmp;\n" +"ADD score1, score1, tmp;\n" +"SUB tmp, d1, m1;\n" +"ABS tmp, tmp;\n" +"ADD score1, score1, tmp;\n" + +"SUB score2, a1, l1;\n" +"ABS score2, score2;\n" +"SUB tmp, b1, m1;\n" +"ABS tmp, tmp;\n" +"ADD score2, score2, tmp;\n" +"SUB tmp, c1, n1;\n" +"ABS tmp, tmp;\n" +"ADD score2, score2, tmp;\n" + +"SUB score3, d1, i1;\n" +"ABS score3, score3;\n" +"SUB tmp, e1, j1;\n" +"ABS tmp, tmp;\n" +"ADD score3, score3, tmp;\n" +"SUB tmp, f1, k1;\n" +"ABS tmp, tmp;\n" +"ADD score3, score3, tmp;\n" + +"SUB score4, e1, h1;\n" +"ABS score4, score4;\n" +"SUB tmp, f1, i1;\n" +"ABS tmp, tmp;\n" +"ADD score4, score4, tmp;\n" +"SUB tmp, g1, j1;\n" +"ABS tmp, tmp;\n" +"ADD score4, score4, tmp;\n" +"SUB if1, sscore, score1;\n" +"SUB if2, score1, score2;\n" +"CMP if2, if1, -1.0, if2;\n" +"CMP spred1, if1, spred1, spred2;\n" +"CMP spred1, if2, spred1, spred3;\n" +"CMP sscore, if1, sscore, score1;\n" +"CMP sscore, if2, sscore, score2;\n" +"SUB if1, sscore, score3;\n" +"SUB if2, score3, score4;\n" +"CMP if2, if1, -1.0, if2;\n" +"CMP spred1, if1, spred1, spred4;\n" +"CMP spred1, if2, spred1, spred5;\n" +"ADD spred4, p2, diff0;\n" +"SUB spred5, p2, diff0;\n" +"SUB if1, spred4, spred1;\n" +"SUB if2, spred1, spred5;\n" +"CMP spred1, if1, spred4, spred1;\n" +"CMP spred1, if2, spred5, spred1;\n"; + +static const QString yadif[2] = { +yadif_setup + +"TEMP d;\n" +"ALIAS i = current;\n" +"TEX current, tex, texture[1], %1;\n" +"TEX d, tex, texture[2], %1;\n" +"ADD tmp, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX a, tmp, texture[2], %1;\n" +"TEX f, tmp, texture[1], %1;\n" +"TEX k, tmp, texture[0], %1;\n" +"ADD tmp, tex, {0.0, %4, 0.0, 0.0};\n" +"TEX c, tmp, texture[2], %1;\n" +"TEX h, tmp, texture[1], %1;\n" +"SUB tmp, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX b, tmp, texture[2], %1;\n" +"TEX g, tmp, texture[1], %1;\n" +"TEX l, tmp, texture[0], %1;\n" +"SUB tmp, tex, {0.0, %4, 0.0, 0.0};\n" +"TEX e, tmp, texture[2], %1;\n" +"TEX j, tmp, texture[1], %1;\n" ++ yadif_spatial_sample ++ yadif_calc ++ field_calc + +"CMP res, prev, current, spred1;\n" +, +yadif_setup + +"TEMP i;\n" +"ALIAS d = current;\n" +"TEX current, tex, texture[1], %1;\n" +"TEX i, tex, texture[0], %1;\n" +"ADD tmp, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX a, tmp, texture[2], %1;\n" +"TEX f, tmp, texture[1], %1;\n" +"TEX k, tmp, texture[0], %1;\n" +"ADD tmp, tex, {0.0, %4, 0.0, 0.0};\n" +"TEX c, tmp, texture[1], %1;\n" +"TEX h, tmp, texture[0], %1;\n" +"SUB tmp, tex, {0.0, %3, 0.0, 0.0};\n" +"TEX b, tmp, texture[2], %1;\n" +"TEX g, tmp, texture[1], %1;\n" +"TEX l, tmp, texture[0], %1;\n" +"SUB tmp, tex, {0.0, %4, 0.0, 0.0};\n" +"TEX e, tmp, texture[1], %1;\n" +"TEX j, tmp, texture[0], %1;\n" ++ yadif_spatial_sample ++ yadif_calc ++ field_calc + +"CMP res, prev, spred1, current;\n" +}; + +static const QString bicubic = +"TEMP coord, coord2, cdelta, parmx, parmy, a, b, c, d;\n" +"MAD coord.xy, fragment.texcoord[0], {%6, %7}, {0.5, 0.5};\n" +"TEX parmx, coord.x, texture[1], 1D;\n" +"TEX parmy, coord.y, texture[1], 1D;\n" +"MUL cdelta.xz, parmx.rrgg, {-%5, 0, %5, 0};\n" +"MUL cdelta.yw, parmy.rrgg, {0, -%3, 0, %3};\n" +"ADD coord, fragment.texcoord[0].xyxy, cdelta.xyxw;\n" +"ADD coord2, fragment.texcoord[0].xyxy, cdelta.zyzw;\n" +"TEX a, coord.xyxy, texture[0], 2D;\n" +"TEX b, coord.zwzw, texture[0], 2D;\n" +"TEX c, coord2.xyxy, texture[0], 2D;\n" +"TEX d, coord2.zwzw, texture[0], 2D;\n" +"LRP a, parmy.b, a, b;\n" +"LRP c, parmy.b, c, d;\n" +"LRP result.color, parmx.b, a, c;\n"; + +QString OpenGLVideo::GetProgramString(OpenGLFilterType name, + QString deint, FrameScanType field) { QString ret = "!!ARBfp1.0\n" - "OPTION ARB_precision_hint_fastest;"; + "OPTION ARB_precision_hint_fastest;\n"; switch (name) { case kGLFilterYUV2RGB: - ret = ret + yuv2rgb1a + yuv2rgb1c; - if (useColourControl) - ret += yuv2rgb2; - ret += yuv2rgb3; - ret += frameBuffer ? yuv2rgb5 : yuv2rgb4; + { + bool need_tex = true; + QString deint_bit = ""; + if (deint != "") + { + uint tmp_field = 0; + if (field == kScan_Intr2ndField) + tmp_field = 1; + if (deint == "openglbobdeint" || + deint == "openglonefield" || + deint == "opengldoubleratefieldorder") + { + deint_bit = bobdeint[tmp_field]; + } + else if (deint == "opengldoublerateonefield") + { + deint_bit = doublerateonefield[tmp_field]; + if (!tmp_field) { need_tex = false; } + } + else if (deint == "opengllinearblend" || + deint == "opengldoubleratelinearblend") + { + deint_bit = linearblend[tmp_field]; + if (!tmp_field) { need_tex = false; } + } + else if (deint == "openglkerneldeint" || + deint == "opengldoubleratekerneldeint") + { + deint_bit = kerneldeint[tmp_field]; + if (!tmp_field) { need_tex = false; } + } + else if (deint == "openglyadif" || + deint == "opengldoublerateyadif") + { + deint_bit = yadif[tmp_field]; + need_tex = false; + } + else + { + VERBOSE(VB_PLAYBACK, LOC + + "Unrecognised OpenGL deinterlacer"); + } + } + + ret += attrib_fast; + ret += useColourControl ? param_colour : ""; + ret += (deint != "") ? var_deint : ""; + ret += var_fast + (need_tex ? tex_fast : ""); + ret += deint_bit; + ret += useColourControl ? calc_colour_fast : ""; + ret += end_fast; + } break; - case kGLFilterYUV2RGBA: - ret = ret + yuv2rgb1a + yuv2rgb1b + yuv2rgb1c; - if (useColourControl) - ret += yuv2rgb2; - ret = ret + yuv2rgb3 + yuv2rgb4 + yuv2rgb6; - break; - case kGLFilterKernelDeint: - ret += - "ATTRIB tex = fragment.texcoord[0];" - "PARAM off = program.env[0];" - "TEMP sam, pos, cum, cur, field, mov;" - "RCP field, off.x;" - "MUL field, tex.yyyy, field;" - "FRC field, field;" - "SUB field, field, 0.5;" - "TEX sam, tex, texture[1], %1;" - "TEX cur, tex, texture[0], %1;" - "SUB mov, cur, sam;" - "MUL cum, sam, 0.125;" - "MAD cum, cur, 0.125, cum;" - "ABS mov, mov;" - "SUB mov, mov, 0.12;" - "ADD pos, tex, off.wyww;" - "TEX sam, pos, texture[0], %1;" - "MAD cum, sam, 0.5, cum;" - "SUB pos, tex, off.wyww;" - "TEX sam, pos, texture[0], %1;" - "MAD cum, sam, 0.5, cum;" - "MAD pos, off.wyww, 2.0, tex;" - "TEX sam, pos, texture[0], %1;" - "MAD cum, sam, -0.0625, cum;" - "TEX sam, pos, texture[1], %1;" - "MAD cum, sam, -0.0625, cum;" - "MAD pos, off.wyww, -2.0, tex;" - "TEX sam, pos, texture[0], %1;" - "MAD cum, sam, -0.0625, cum;" - "TEX sam, pos, texture[1], %1;" - "MAD cum, sam, -0.0625, cum;" - "CMP cum, mov, cur, cum;" - "CMP result.color, field, cum, cur;"; - break; + ret += attrib_fast; + ret += useColourControl ? param_colour : ""; + ret += var_fast + tex_fast + calc_fast_alpha; + ret += useColourControl ? calc_colour_fast : ""; + ret += end_fast_alpha; - case kGLFilterLinearBlendDeintDFR: - ret += - "ATTRIB tex = fragment.texcoord[0];" - "PARAM off = program.env[0];" - "TEMP field, top, bot, current, previous, next, other, mov;" - "TEX next, tex, texture[0], %1;" - "TEX current, tex, texture[1], %1;" - "TEX previous, tex, texture[2], %1;" - "ADD top, tex, off.wyww;" - "TEX other, top, texture[1], %1;" - "SUB top, tex, off.wyww;" - "TEX bot, top, texture[1], %1;" - "LRP other, 0.5, other, bot;" - "RCP field, off.x;" - "MUL field, tex.yyyy, field;" - "FRC field, field;" - "SUB field, field, 0.5;" - "SUB top, current, next;" - "SUB bot, current, previous;" - "CMP mov, field, bot, top;" - "ABS mov, mov;" - "SUB mov, mov, 0.12;" - "CMP other, mov, current, other;" - "CMP top, field, other, current;" - "CMP bot, field, current, other;" - "CMP result.color, off.y, top, bot;"; break; - case kGLFilterOneFieldDeintDFR: - ret += - "ATTRIB tex = fragment.texcoord[0];" - "PARAM off = program.env[0];" - "TEMP field, top, bot, current, previous, next, other, mov;" - "TEX next, tex, texture[0], %1;" - "TEX current, tex, texture[1], %1;" - "TEX previous, tex, texture[2], %1;" - "ADD top, tex, off.wyww;" - "TEX other, top, texture[1], %1;" - "RCP field, off.x;" - "MUL field, tex.yyyy, field;" - "FRC field, field;" - "SUB field, field, 0.5;" - "SUB top, current, next;" - "SUB bot, current, previous;" - "CMP mov, field, bot, top;" - "ABS mov, mov;" - "SUB mov, mov, 0.12;" - "CMP other, mov, current, other;" - "CMP top, field, other, current;" - "CMP bot, field, current, other;" - "CMP result.color, off.y, top, bot;"; + case kGLFilterNone: + case kGLFilterResize: break; - case kGLFilterKernelDeintDFR: - ret += - "ATTRIB tex = fragment.texcoord[0];" - "PARAM off = program.env[0];" - "TEMP sam, pos, bot, top, cur, pre, nex, field, mov;" - "RCP field, off.x;" - "MUL field, tex.yyyy, field;" - "FRC field, field;" - "SUB field, field, 0.5;" - "TEX pre, tex, texture[2], %1;" // -1,0 - "TEX cur, tex, texture[1], %1;" // 0,0 - "TEX nex, tex, texture[0], %1;" // +1,0 - "SUB top, nex, cur;" - "SUB bot, pre, cur;" - "CMP mov, field, bot, top;" - "ABS mov, mov;" - "SUB mov, mov, 0.12;" - "MUL bot, pre, 0.125;" // BOT -1,0 - "MAD bot, cur, 0.125, bot;" // BOT +1,0 - "MUL top, cur, 0.125;" // TOP -1,0 - "MAD top, nex, 0.125, top;" // TOP +1,0 - "ADD pos, tex, off.wyww;" - "TEX sam, pos, texture[1], %1;" // 0,+1 - "MAD bot, sam, 0.5, bot;" // BOT 0,+1 - "MAD top, sam, 0.5, top;" // TOP 0,+1 - "SUB pos, tex, off.wyww;" - "TEX sam, pos, texture[1], %1;" // 0,-1 - "MAD bot, sam, 0.5, bot;" // BOT 0,-1 - "MAD top, sam, 0.5, top;" // TOP 0,-1 - "MAD pos, off.wyww, 2.0, tex;" - "TEX sam, pos, texture[1], %1;" // 0,+2 - "MAD bot, sam, -0.0625, bot;" // BOT +1,+2 - "MAD top, sam, -0.0625, top;" // TOP -1,+2 - "TEX sam, pos, texture[2], %1;" // -1,+2 - "MAD bot, sam, -0.0625, bot;" // BOT -1,+2 - "TEX sam, pos, texture[0], %1;" // +1,+2 - "MAD top, sam, -0.0625, top;" // TOP +1,+2 - "MAD pos, off.wyww, -2.0, tex;" - "TEX sam, pos, texture[1], %1;" // +1,-2 - "MAD bot, sam, -0.0625, bot;" // BOT +1,-2 - "MAD top, sam, -0.0625, top;" // TOP -1,-2 - "TEX sam, pos, texture[2], %1;" // -1, -2 row - "MAD bot, sam, -0.0625, bot;" // BOT -1,-2 - "TEX sam, pos, texture[0], %1;" // +1,-2 - "MAD top, sam, -0.0625, top;" // TOP +1,-2 - "CMP top, mov, cur, top;" - "CMP bot, mov, cur, bot;" - "CMP top, field, top, cur;" - "CMP bot, field, cur, bot;" - "CMP result.color, off.y, top, bot;"; + case kGLFilterBicubic: + + ret += bicubic; break; - case kGLFilterBobDeintDFR: - case kGLFilterOneFieldDeint: - ret += - "ATTRIB tex = fragment.texcoord[0];" - "PARAM off = program.env[0];" - "TEMP field, top, bottom, current, other;" - "TEX current, tex, texture[0], %1;" - "RCP field, off.x;" - "MUL field, tex.yyyy, field;" - "FRC field, field;" - "SUB field, field, 0.5;" - "ADD top, tex, off.wyww;" - "TEX other, top, texture[0], %1;" - "CMP top, field, other, current;" - "CMP bottom, field, current, other;" - "CMP result.color, off.y, top, bottom;"; + default: + VERBOSE(VB_PLAYBACK, LOC_ERR + "Unknown fragment program."); break; + } - case kGLFilterLinearBlendDeint: - ret += - "ATTRIB tex = fragment.texcoord[0];" - "PARAM off = program.env[0];" - "TEMP mov, field, cur, pre, pos;" - "RCP field, off.x;" - "MUL field, tex.yyyy, field;" - "FRC field, field;" - "SUB field, field, 0.5;" - "TEX cur, tex, texture[0], %1;" - "TEX pre, tex, texture[1], %1;" - "SUB mov, cur, pre;" - "ABS mov, mov;" - "SUB mov, mov, 0.12;" - "ADD pos, tex, off.wyww;" - "TEX pre, pos, texture[0], %1;" - "SUB pos, tex, off.wyww;" - "TEX pos, pos, texture[0], %1;" - "LRP pre, 0.5, pos, pre;" - "CMP pre, field, pre, cur;" - "CMP result.color, mov, cur, pre;"; - break; + QString temp = textureRects ? "RECT" : "2D"; + ret.replace("%1", temp); - case kGLFilterFieldOrderDFR: - ret += - "ATTRIB tex = fragment.texcoord[0];" - "PARAM off = program.env[0];" - "TEMP field, cur, pre, bot;" - "TEX cur, tex, texture[0], %1;" - "TEX pre, tex, texture[1], %1;" - "RCP field, off.x;" - "MUL field, tex.yyyy, field;" - "FRC field, field;" - "SUB field, field, 0.5;" - "CMP bot, off.y, pre, cur;" - "CMP result.color, field, bot, cur;"; + float lineHeight = 1.0f; + float colWidth = 1.0f; + QSize fb_size = GetTextureSize(video_dim); - break; + if (!textureRects && + (inputTextureSize.height() > 0)) + { + lineHeight /= inputTextureSize.height(); + colWidth /= inputTextureSize.width(); + } - case kGLFilterNone: - case kGLFilterResize: - break; + float fieldSize = 1.0f / (lineHeight * 2.0); - default: - VERBOSE(VB_PLAYBACK, LOC_ERR + "Unknown fragment program."); - break; + ret.replace("%2", temp.setNum(fieldSize, 'f', 8)); + ret.replace("%3", temp.setNum(lineHeight, 'f', 8)); + ret.replace("%4", temp.setNum(lineHeight * 2.0, 'f', 8)); + ret.replace("%5", temp.setNum(colWidth, 'f', 8)); + ret.replace("%6", temp.setNum((float)fb_size.width(), 'f', 1)); + ret.replace("%7", temp.setNum((float)fb_size.height(), 'f', 1)); + + ret += "END"; + + VERBOSE(VB_PLAYBACK, LOC + QString("Created %1 fragment program %2") + .arg(FilterToString(name)).arg(deint)); + + return ret; +} + +uint OpenGLVideo::ParseOptions(QString options) +{ + uint ret = kGLMaxFeat - 1; + + QStringList list = QStringList::split(",", options); + + if (list.empty()) + return ret; + + for (QStringList::Iterator i = list.begin(); + i != list.end(); ++i) + { + QString name = (*i).section('=', 0, 0); + QString opts = (*i).section('=', 1); + + if (name == "opengloptions") + { + if (opts.contains("nofinish")) + ret -= kGLFinish; + if (opts.contains("nofence")) + ret -= kGLNVFence; + if (opts.contains("nopbo")) + ret -= kGLExtPBufObj; + if (opts.contains("nopbuf")) + ret -= kGLXPBuffer; + if (opts.contains("nofbo")) + ret -= kGLExtFBufObj; + if (opts.contains("nofrag")) + ret -= kGLExtFragProg; + if (opts.contains("norect")) + ret -= kGLExtRect; + return ret; + } } - return ret + "END"; + return ret; } + Index: mythtv/libs/libmythtv/filtermanager.cpp =================================================================== --- mythtv/libs/libmythtv/filtermanager.cpp (revision 17692) +++ mythtv/libs/libmythtv/filtermanager.cpp (working copy) @@ -171,6 +171,10 @@ { QString FiltName = (*i).section('=', 0, 0); QString FiltOpts = (*i).section('=', 1); + + if (FiltName.contains("opengl")) + continue; + FI = GetFilterInfoByName(FiltName); if (FI) Index: mythtv/libs/libmythtv/xvmctextures.cpp =================================================================== --- mythtv/libs/libmythtv/xvmctextures.cpp (revision 17692) +++ mythtv/libs/libmythtv/xvmctextures.cpp (working copy) @@ -88,7 +88,7 @@ XVisualInfo *vis_info; vis_info = glXGetVisualFromFBConfig(XJ_disp, glx_fbconfig); gl_window = get_gl_window(XJ_disp, XJ_curwin, vis_info, - window_size, true); + QRect(QPoint(0,0), window_size)); glx_window = get_glx_window(XJ_disp, glx_fbconfig, gl_window, glx_context, glx_pbuffer, window_size); Index: mythtv/libs/libmythtv/util-opengl.cpp =================================================================== --- mythtv/libs/libmythtv/util-opengl.cpp (revision 17692) +++ mythtv/libs/libmythtv/util-opengl.cpp (working copy) @@ -3,6 +3,19 @@ #include "util-opengl.h" #include "frame.h" +#ifdef MMX +extern "C" { +#include "libavcodec/i386/mmx.h" +} +#endif + +PFNGLMAPBUFFERPROC gMythGLMapBufferARB = NULL; +PFNGLBINDBUFFERARBPROC gMythGLBindBufferARB = NULL; +PFNGLGENBUFFERSARBPROC gMythGLGenBuffersARB = NULL; +PFNGLBUFFERDATAARBPROC gMythGLBufferDataARB = NULL; +PFNGLUNMAPBUFFERARBPROC gMythGLUnmapBufferARB = NULL; +PFNGLDELETEBUFFERSARBPROC gMythGLDeleteBuffersARB = NULL; + PFNGLGENPROGRAMSARBPROC gMythGLGenProgramsARB = NULL; PFNGLBINDPROGRAMARBPROC gMythGLBindProgramARB = NULL; PFNGLPROGRAMSTRINGARBPROC gMythGLProgramStringARB = NULL; @@ -19,6 +32,11 @@ PFNGLXGETVIDEOSYNCSGIPROC gMythGLXGetVideoSyncSGI = NULL; PFNGLXWAITVIDEOSYNCSGIPROC gMythGLXWaitVideoSyncSGI = NULL; +PFNGLGENFENCESNVPROC gMythGLGenFencesNV = NULL; +PFNGLDELETEFENCESNVPROC gMythGLDeleteFencesNV = NULL; +PFNGLSETFENCENVPROC gMythGLSetFenceNV = NULL; +PFNGLFINISHFENCENVPROC gMythGLFinishFenceNV = NULL; + bool init_opengl(void) { static bool is_initialized = false; @@ -30,6 +48,19 @@ is_initialized = true; + gMythGLMapBufferARB = (PFNGLMAPBUFFERPROC) + get_gl_proc_address("glMapBufferARB"); + gMythGLBindBufferARB = (PFNGLBINDBUFFERARBPROC) + get_gl_proc_address("glBindBufferARB"); + gMythGLGenBuffersARB = (PFNGLGENBUFFERSARBPROC) + get_gl_proc_address("glGenBuffersARB"); + gMythGLBufferDataARB = (PFNGLBUFFERDATAARBPROC) + get_gl_proc_address("glBufferDataARB"); + gMythGLUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC) + get_gl_proc_address("glUnmapBufferARB"); + gMythGLDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) + get_gl_proc_address("glDeleteBuffersARB"); + gMythGLGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) get_gl_proc_address("glGenProgramsARB"); gMythGLBindProgramARB = (PFNGLBINDPROGRAMARBPROC) @@ -60,6 +91,15 @@ gMythGLXWaitVideoSyncSGI = (PFNGLXWAITVIDEOSYNCSGIPROC) get_gl_proc_address("glXWaitVideoSyncSGI"); + gMythGLGenFencesNV = (PFNGLGENFENCESNVPROC) + get_gl_proc_address("glGenFencesNV"); + gMythGLDeleteFencesNV = (PFNGLDELETEFENCESNVPROC) + get_gl_proc_address("glDeleteFencesNV"); + gMythGLSetFenceNV = (PFNGLSETFENCENVPROC) + get_gl_proc_address("glSetFenceNV"); + gMythGLFinishFenceNV = (PFNGLFINISHFENCENVPROC) + get_gl_proc_address("glFinishFenceNV"); + return true; } @@ -101,7 +141,7 @@ if (!ret) return false; - + gl_minor=2; static_major = major = gl_major; static_minor = minor = gl_minor; static_ret = true; @@ -210,8 +250,7 @@ Window get_gl_window(Display *XJ_disp, Window XJ_curwin, XVisualInfo *visInfo, - const QSize &window_size, - bool map_window) + const QRect &window_rect) { X11L; @@ -220,11 +259,11 @@ XJ_disp, XJ_curwin, visInfo->visual, AllocNone); Window gl_window = XCreateWindow( - XJ_disp, XJ_curwin, 0, 0, window_size.width(), window_size.height(), 0, + XJ_disp, XJ_curwin, window_rect.x(), window_rect.y(), + window_rect.width(), window_rect.height(), 0, visInfo->depth, InputOutput, visInfo->visual, CWColormap, &attributes); - if (map_window) - XMapWindow(XJ_disp, gl_window); + XMapWindow(XJ_disp, gl_window); XFree(visInfo); @@ -267,41 +306,6 @@ return glx_window; } -void copy_pixels_to_texture(const unsigned char *buf, - int buffer_format, - const QSize &buffer_size, - int texture, - int texture_type) -{ - glBindTexture(texture_type, texture); - - uint format; - switch (buffer_format) - { - case FMT_YV12: - format = GL_LUMINANCE; - break; - case FMT_RGB24: - format = GL_RGB; - break; - case FMT_RGBA32: - format = GL_RGBA; - break; - case FMT_ALPHA: - format = GL_ALPHA; - break; - default: - return; - } - - glTexSubImage2D( - texture_type, - 0, 0, 0, - buffer_size.width(), buffer_size.height(), - format, GL_UNSIGNED_BYTE, - buf); -} - __GLXextFuncPtr get_gl_proc_address(const QString &procName) { __GLXextFuncPtr ret = NULL; @@ -371,3 +375,596 @@ return gMythGLXGetVideoSyncSGI && gMythGLXWaitVideoSyncSGI; } + +bool has_gl_pixelbuffer_object_support(const QString &ext) +{ + init_opengl(); + + if (!ext.contains("GL_ARB_pixel_buffer_object")) + return false; + + return (gMythGLMapBufferARB && + gMythGLBindBufferARB && + gMythGLGenBuffersARB && + gMythGLDeleteBuffersARB && + gMythGLBufferDataARB && + gMythGLUnmapBufferARB); +} + +bool has_gl_nvfence_support(const QString &ext) +{ + init_opengl(); + + if (!ext.contains("GL_NV_fence")) + return false; + + return (gMythGLGenFencesNV && + gMythGLDeleteFencesNV && + gMythGLSetFenceNV && + gMythGLFinishFenceNV); +} + +#ifdef MMX +static inline void mmx_pack_alpha_high(uint8_t *a1, uint8_t *a2, + uint8_t *y1, uint8_t *y2) +{ + movq_m2r (*a1, mm4); + punpckhbw_m2r (*y1, mm4); + movq_m2r (*a2, mm7); + punpckhbw_m2r (*y2, mm7); +} + +static inline void mmx_pack_alpha_low(uint8_t *a1, uint8_t *a2, + uint8_t *y1, uint8_t *y2) +{ + movq_m2r (*a1, mm4); + punpcklbw_m2r (*y1, mm4); + movq_m2r (*a2, mm7); + punpcklbw_m2r (*y2, mm7); +} + +static mmx_t mmx_1s = {0xffffffffffffffffLL}; + +static inline void mmx_pack_alpha1s_high(uint8_t *y1, uint8_t *y2) +{ + movq_m2r (mmx_1s, mm4); + punpckhbw_m2r (*y1, mm4); + movq_m2r (mmx_1s, mm7); + punpckhbw_m2r (*y2, mm7); +} + +static inline void mmx_pack_alpha1s_low(uint8_t *y1, uint8_t *y2) +{ + movq_m2r (mmx_1s, mm4); + punpcklbw_m2r (*y1, mm4); + movq_m2r (mmx_1s, mm7); + punpcklbw_m2r (*y2, mm7); +} + +static inline void mmx_pack_middle(uint8_t *dest1, uint8_t *dest2) +{ + movq_r2r (mm3, mm5); + punpcklbw_r2r (mm2, mm5); + + movq_r2r (mm5, mm6); + punpcklbw_r2r (mm4, mm6); + movq_r2m (mm6, *(dest1)); + + movq_r2r (mm5, mm6); + punpckhbw_r2r (mm4, mm6); + movq_r2m (mm6, *(dest1 + 8)); + + movq_r2r (mm5, mm6); + punpcklbw_r2r (mm7, mm6); + movq_r2m (mm6, *(dest2)); + + movq_r2r (mm5, mm6); + punpckhbw_r2r (mm7, mm6); + movq_r2m (mm6, *(dest2 + 8)); +} + +static inline void mmx_pack_end(uint8_t *dest1, uint8_t *dest2) +{ + punpckhbw_r2r (mm2, mm3); + + movq_r2r (mm3, mm6); + punpcklbw_r2r (mm4, mm6); + movq_r2m (mm6, *(dest1 + 16)); + + movq_r2r (mm3, mm6); + punpckhbw_r2r (mm4, mm6); + movq_r2m (mm6, *(dest1 + 24)); + + movq_r2r (mm3, mm6); + punpcklbw_r2r (mm7, mm6); + movq_r2m (mm6, *(dest2 + 16)); + + punpckhbw_r2r (mm7, mm3); + movq_r2m (mm3, *(dest2 + 24)); +} + +static inline void mmx_pack_easy(uint8_t *dest, uint8_t *y) +{ + movq_m2r (mmx_1s, mm4); + punpcklbw_m2r (*y, mm4); + + movq_r2r (mm3, mm5); + punpcklbw_r2r (mm2, mm5); + + movq_r2r (mm5, mm6); + punpcklbw_r2r (mm4, mm6); + movq_r2m (mm6, *(dest)); + + movq_r2r (mm5, mm6); + punpckhbw_r2r (mm4, mm6); + movq_r2m (mm6, *(dest + 8)); + + movq_m2r (mmx_1s, mm4); + punpckhbw_m2r (*y, mm4); + + punpckhbw_r2r (mm2, mm3); + + movq_r2r (mm3, mm6); + punpcklbw_r2r (mm4, mm6); + movq_r2m (mm6, *(dest + 16)); + + punpckhbw_r2r (mm4, mm3); + movq_r2m (mm3, *(dest + 24)); +} + +static mmx_t mmx_0s = {0x0000000000000000LL}; +static mmx_t round = {0x0002000200020002LL}; + +static inline void mmx_interp_start(uint8_t *left, uint8_t *right) +{ + movd_m2r (*left, mm5); + punpcklbw_m2r (mmx_0s, mm5); + + movq_r2r (mm5, mm4); + paddw_r2r (mm4, mm4); + paddw_r2r (mm5, mm4); + paddw_m2r (round, mm4); + + movd_m2r (*right, mm5); + punpcklbw_m2r (mmx_0s, mm5); + paddw_r2r (mm5, mm4); + + psrlw_i2r (2, mm4); +} + +static inline void mmx_interp_endu(void) +{ + movq_r2r (mm4, mm2); + psllw_i2r (8, mm2); + paddb_r2r (mm4, mm2); +} + +static inline void mmx_interp_endv(void) +{ + movq_r2r (mm4, mm3); + psllw_i2r (8, mm3); + paddb_r2r (mm4, mm3); +} + +static inline void mmx_pack_chroma(uint8_t *u, uint8_t *v) +{ + movd_m2r (*u, mm2); + movd_m2r (*v, mm3); + punpcklbw_r2r (mm2, mm2); + punpcklbw_r2r (mm3, mm3); +} +#endif // MMX + +static inline void c_interp(uint8_t *dest, uint8_t *a, uint8_t *b, + uint8_t *c, uint8_t *d) +{ + unsigned int tmp = (unsigned int) *a; + tmp *= 3; + tmp += 2; + tmp += (unsigned int) *c; + dest[0] = (uint8_t) (tmp >> 2); + + tmp = (unsigned int) *b; + tmp *= 3; + tmp += 2; + tmp += (unsigned int) *d; + dest[1] = (uint8_t) (tmp >> 2); + + tmp = (unsigned int) *c; + tmp *= 3; + tmp += 2; + tmp += (unsigned int) *a; + dest[2] = (uint8_t) (tmp >> 2); + + tmp = (unsigned int) *d; + tmp *= 3; + tmp += 2; + tmp += (unsigned int) *b; + dest[3] = (uint8_t) (tmp >> 2); +} + +void pack_yv12alpha(const unsigned char *source, + const unsigned char *dest, + const int *offsets, + const int *pitches, + const QSize size, + const unsigned char *alpha) +{ + const int width = size.width(); + const int height = size.height(); + + if (height % 2 || width % 2) + return; + +#ifdef MMX + int residual = width % 8; + int mmx_width = width - residual; + int c_start_w = mmx_width; +#else + int residual = 0; + int mmx_width = width; + int c_start_w = 0; +#endif + + uint bgra_width = width << 2; + uint chroma_width = width >> 1; + + uint y_extra = (pitches[0] << 1) - width + residual; + uint u_extra = pitches[1] - chroma_width + (residual >> 1); + uint v_extra = pitches[2] - chroma_width + (residual >> 1); + uint d_extra = bgra_width + (residual << 2); + + uint8_t *ypt_1 = (uint8_t *)source + offsets[0]; + uint8_t *ypt_2 = ypt_1 + pitches[0]; + uint8_t *upt = (uint8_t *)source + offsets[1]; + uint8_t *vpt = (uint8_t *)source + offsets[2]; + uint8_t *dst_1 = (uint8_t *) dest; + uint8_t *dst_2 = dst_1 + bgra_width; + + if (alpha) + { + uint8_t *alpha_1 = (uint8_t *) alpha; + uint8_t *alpha_2 = alpha_1 + width; + uint a_extra = width + residual; + +#ifdef MMX + for (int row = 0; row < height; row += 2) + { + for (int col = 0; col < mmx_width; col += 8) + { + mmx_pack_chroma(upt, vpt); + mmx_pack_alpha_low(alpha_1, alpha_2, ypt_1, ypt_2); + mmx_pack_middle(dst_1, dst_2); + mmx_pack_alpha_high(alpha_1, alpha_2, ypt_1, ypt_2); + mmx_pack_end(dst_1, dst_2); + + dst_1 += 32; dst_2 += 32; + alpha_1 += 8; alpha_2 += 8; + ypt_1 += 8; ypt_2 += 8; + upt += 4; vpt += 4; + } + + ypt_1 += y_extra; ypt_2 += y_extra; + upt += u_extra; vpt += v_extra; + dst_1 += d_extra; dst_2 += d_extra; + alpha_1 += a_extra; alpha_2 += a_extra; + } + + emms(); + + if (residual) + { + y_extra = (pitches[0] << 1) - width + mmx_width; + u_extra = pitches[1] - chroma_width + (mmx_width >> 1); + v_extra = pitches[2] - chroma_width + (mmx_width >> 1); + d_extra = bgra_width + (mmx_width << 2); + + ypt_1 = (uint8_t *)source + offsets[0] + mmx_width; + ypt_2 = ypt_1 + pitches[0]; + upt = (uint8_t *)source + offsets[1] + (mmx_width>>1); + vpt = (uint8_t *)source + offsets[2] + (mmx_width>>1); + dst_1 = (uint8_t *) dest + (mmx_width << 2); + dst_2 = dst_1 + bgra_width; + + alpha_1 = (uint8_t *) alpha + mmx_width; + alpha_2 = alpha_1 + width; + a_extra = width + mmx_width; + } + else + { + return; + } +#endif //MMX + + for (int row = 0; row < height; row += 2) + { + for (int col = c_start_w; col < width; col += 2) + { + *(dst_1++) = *vpt; *(dst_2++) = *vpt; + *(dst_1++) = *(alpha_1++); + *(dst_2++) = *(alpha_2++); + *(dst_1++) = *upt; *(dst_2++) = *upt; + *(dst_1++) = *(ypt_1++); + *(dst_2++) = *(ypt_2++); + + *(dst_1++) = *vpt; *(dst_2++) = *(vpt++); + *(dst_1++) = *(alpha_1++); + *(dst_2++) = *(alpha_2++); + *(dst_1++) = *upt; *(dst_2++) = *(upt++); + *(dst_1++) = *(ypt_1++); + *(dst_2++) = *(ypt_2++); + } + + ypt_1 += y_extra; ypt_2 += y_extra; + upt += u_extra; vpt += v_extra; + alpha_1 += a_extra; alpha_2 += a_extra; + dst_1 += d_extra; dst_2 += d_extra; + } + } + else + { + +#ifdef MMX + for (int row = 0; row < height; row += 2) + { + for (int col = 0; col < mmx_width; col += 8) + { + mmx_pack_chroma(upt, vpt); + mmx_pack_alpha1s_low(ypt_1, ypt_2); + mmx_pack_middle(dst_1, dst_2); + mmx_pack_alpha1s_high(ypt_1, ypt_2); + mmx_pack_end(dst_1, dst_2); + + dst_1 += 32; dst_2 += 32; + ypt_1 += 8; ypt_2 += 8; + upt += 4; vpt += 4; + + } + ypt_1 += y_extra; ypt_2 += y_extra; + upt += u_extra; vpt += v_extra; + dst_1 += d_extra; dst_2 += d_extra; + } + + emms(); + + if (residual) + { + y_extra = (pitches[0] << 1) - width + mmx_width; + u_extra = pitches[1] - chroma_width + (mmx_width >> 1); + v_extra = pitches[2] - chroma_width + (mmx_width >> 1); + d_extra = bgra_width + (mmx_width << 2); + + ypt_1 = (uint8_t *)source + offsets[0] + mmx_width; + ypt_2 = ypt_1 + pitches[0]; + upt = (uint8_t *)source + offsets[1] + (mmx_width>>1); + vpt = (uint8_t *)source + offsets[2] + (mmx_width>>1); + dst_1 = (uint8_t *) dest + (mmx_width << 2); + dst_2 = dst_1 + bgra_width; + } + else + { + return; + } +#endif //MMX + + for (int row = 0; row < height; row += 2) + { + for (int col = c_start_w; col < width; col += 2) + { + *(dst_1++) = *vpt; *(dst_2++) = *vpt; + *(dst_1++) = 255; *(dst_2++) = 255; + *(dst_1++) = *upt; *(dst_2++) = *upt; + *(dst_1++) = *(ypt_1++); + *(dst_2++) = *(ypt_2++); + + *(dst_1++) = *vpt; *(dst_2++) = *(vpt++); + *(dst_1++) = 255; *(dst_2++) = 255; + *(dst_1++) = *upt; *(dst_2++) = *(upt++); + *(dst_1++) = *(ypt_1++); + *(dst_2++) = *(ypt_2++); + } + ypt_1 += y_extra; ypt_2 += y_extra; + upt += u_extra; vpt += v_extra; + dst_1 += d_extra; dst_2 += d_extra; + } + } +} + +void pack_yv12interlaced(const unsigned char *source, + const unsigned char *dest, + const int *offsets, + const int *pitches, + const QSize size) +{ + int width = size.width(); + int height = size.height(); + + if (height % 4 || width % 2) + return; + + uint bgra_width = width << 2; + uint dwrap = (bgra_width << 2) - bgra_width; + uint chroma_width = width >> 1; + uint ywrap = (pitches[0] << 1) - width; + uint uwrap = (pitches[1] << 1) - chroma_width; + uint vwrap = (pitches[2] << 1) - chroma_width; + + uint8_t *ypt_1 = (uint8_t *)source + offsets[0]; + uint8_t *ypt_2 = ypt_1 + pitches[0]; + uint8_t *ypt_3 = ypt_1 + (pitches[0] * (height - 2)); + uint8_t *ypt_4 = ypt_3 + pitches[0]; + + uint8_t *u1 = (uint8_t *)source + offsets[1]; + uint8_t *v1 = (uint8_t *)source + offsets[2]; + uint8_t *u2 = u1 + pitches[1]; uint8_t *v2 = v1 + pitches[2]; + uint8_t *u3 = u1 + (pitches[1] * ((height - 4) >> 1)); + uint8_t *v3 = v1 + (pitches[2] * ((height - 4) >> 1)); + uint8_t *u4 = u3 + pitches[1]; uint8_t *v4 = v3 + pitches[2]; + + uint8_t *dst_1 = (uint8_t *) dest; + uint8_t *dst_2 = dst_1 + bgra_width; + uint8_t *dst_3 = dst_1 + (bgra_width * (height - 2)); + uint8_t *dst_4 = dst_3 + bgra_width; + +#ifdef MMX + + if (!(width % 8)) + { + // pack first 2 and last 2 rows + for (int col = 0; col < width; col += 8) + { + mmx_pack_chroma(u1, v1); + mmx_pack_easy(dst_1, ypt_1); + mmx_pack_chroma(u2, v2); + mmx_pack_easy(dst_2, ypt_2); + mmx_pack_chroma(u3, v3); + mmx_pack_easy(dst_3, ypt_3); + mmx_pack_chroma(u4, v4); + mmx_pack_easy(dst_4, ypt_4); + + dst_1 += 32; dst_2 += 32; dst_3 += 32; dst_4 += 32; + ypt_1 += 8; ypt_2 += 8; ypt_3 += 8; ypt_4 += 8; + u1 += 4; v1 += 4; u2 += 4; v2 += 4; + u3 += 4; v3 += 4; u4 += 4; v4 += 4; + } + + ypt_1 += ywrap; ypt_2 += ywrap; + dst_1 += bgra_width; dst_2 += bgra_width; + + ypt_3 = ypt_2 + pitches[0]; + ypt_4 = ypt_3 + pitches[0]; + dst_3 = dst_2 + bgra_width; + dst_4 = dst_3 + bgra_width; + + ywrap = (pitches[0] << 2) - width; + + u1 = (uint8_t *)source + offsets[1]; + v1 = (uint8_t *)source + offsets[2]; + u2 = u1 + pitches[1]; v2 = v1 + pitches[2]; + u3 = u2 + pitches[1]; v3 = v2 + pitches[2]; + u4 = u3 + pitches[1]; v4 = v3 + pitches[2]; + + height -= 4; + + // pack main body + for (int row = 0 ; row < height; row += 4) + { + for (int col = 0; col < width; col += 8) + { + mmx_interp_start(u1, u3); mmx_interp_endu(); + mmx_interp_start(v1, v3); mmx_interp_endv(); + mmx_pack_easy(dst_1, ypt_1); + + mmx_interp_start(u2, u4); mmx_interp_endu(); + mmx_interp_start(v2, v4); mmx_interp_endv(); + mmx_pack_easy(dst_2, ypt_2); + + mmx_interp_start(u3, u1); mmx_interp_endu(); + mmx_interp_start(v3, v1); mmx_interp_endv(); + mmx_pack_easy(dst_3, ypt_3); + + mmx_interp_start(u4, u2); mmx_interp_endu(); + mmx_interp_start(v4, v2); mmx_interp_endv(); + mmx_pack_easy(dst_4, ypt_4); + + dst_1 += 32; dst_2 += 32; dst_3 += 32; dst_4 += 32; + ypt_1 += 8; ypt_2 += 8; ypt_3 += 8; ypt_4 += 8; + u1 += 4; u2 += 4; u3 += 4; u4 += 4; + v1 += 4; v2 += 4; v3 += 4; v4 += 4; + } + + ypt_1 += ywrap; ypt_2 += ywrap; ypt_3 += ywrap; ypt_4 += ywrap; + dst_1 += dwrap; dst_2 += dwrap; dst_3 += dwrap; dst_4 += dwrap; + u1 += uwrap; v1 += vwrap; u2 += uwrap; v2 += vwrap; + u3 += uwrap; v3 += vwrap; u4 += uwrap;v4 += vwrap; + } + + emms(); + + return; + } +#endif //MMX + + // pack first 2 and last 2 rows + for (int col = 0; col < width; col += 2) + { + *(dst_1++) = *v1; *(dst_2++) = *v2; *(dst_3++) = *v3; *(dst_4++) = *v4; + *(dst_1++) = 255; *(dst_2++) = 255; *(dst_3++) = 255; *(dst_4++) = 255; + *(dst_1++) = *u1; *(dst_2++) = *u2; *(dst_3++) = *u3; *(dst_4++) = *u4; + *(dst_1++) = *(ypt_1++); *(dst_2++) = *(ypt_2++); + *(dst_3++) = *(ypt_3++); *(dst_4++) = *(ypt_4++); + + *(dst_1++) = *(v1++); *(dst_2++) = *(v2++); + *(dst_3++) = *(v3++); *(dst_4++) = *(v4++); + *(dst_1++) = 255; *(dst_2++) = 255; *(dst_3++) = 255; *(dst_4++) = 255; + *(dst_1++) = *(u1++); *(dst_2++) = *(u2++); + *(dst_3++) = *(u3++); *(dst_4++) = *(u4++); + *(dst_1++) = *(ypt_1++); *(dst_2++) = *(ypt_2++); + *(dst_3++) = *(ypt_3++); *(dst_4++) = *(ypt_4++); + } + + ypt_1 += ywrap; ypt_2 += ywrap; + dst_1 += bgra_width; dst_2 += bgra_width; + + ypt_3 = ypt_2 + pitches[0]; + ypt_4 = ypt_3 + pitches[0]; + dst_3 = dst_2 + bgra_width; + dst_4 = dst_3 + bgra_width; + + ywrap = (pitches[0] << 2) - width; + + u1 = (uint8_t *)source + offsets[1]; + v1 = (uint8_t *)source + offsets[2]; + u2 = u1 + pitches[1]; v2 = v1 + pitches[2]; + u3 = u2 + pitches[1]; v3 = v2 + pitches[2]; + u4 = u3 + pitches[1]; v4 = v3 + pitches[2]; + + height -= 4; + + uint8_t v[4], u[4]; + + // pack main body + for (int row = 0; row < height; row += 4) + { + for (int col = 0; col < width; col += 2) + { + c_interp(v, v1, v2, v3, v4); + c_interp(u, u1, u2, u3, u4); + + *(dst_1++) = v[0]; *(dst_2++) = v[1]; + *(dst_3++) = v[2]; *(dst_4++) = v[3]; + *(dst_1++) = 255; *(dst_2++) = 255; *(dst_3++) = 255; *(dst_4++) = 255; + *(dst_1++) = u[0]; *(dst_2++) = u[1]; + *(dst_3++) = u[2]; *(dst_4++) = u[3]; + *(dst_1++) = *(ypt_1++); *(dst_2++) = *(ypt_2++); + *(dst_3++) = *(ypt_3++); *(dst_4++) = *(ypt_4++); + + *(dst_1++) = v[0]; *(dst_2++) = v[1]; + *(dst_3++) = v[2]; *(dst_4++) = v[3]; + *(dst_1++) = 255; *(dst_2++) = 255; *(dst_3++) = 255; *(dst_4++) = 255; + *(dst_1++) = u[0]; *(dst_2++) = u[1]; + *(dst_3++) = u[2]; *(dst_4++) = u[3]; + *(dst_1++) = *(ypt_1++); *(dst_2++) = *(ypt_2++); + *(dst_3++) = *(ypt_3++); *(dst_4++) = *(ypt_4++); + + v1++; v2++; v3++; v4++; + u1++; u2++; u3++; u4++; + } + ypt_1 += ywrap; ypt_2 += ywrap; ypt_3 += ywrap; ypt_4 += ywrap; + u1 += uwrap; u2 += uwrap; u3 += uwrap; u4 += uwrap; + v1 += vwrap; v2 += vwrap; v3 += vwrap; v4 += vwrap; + dst_1 += dwrap; dst_2 += dwrap; dst_3 += dwrap; dst_4 += dwrap; + } +} + +void store_bicubic_weights(float x, float *dst) +{ + float w0 = (((-1 * x + 3) * x - 3) * x + 1) / 6; + float w1 = ((( 3 * x - 6) * x + 0) * x + 4) / 6; + float w2 = (((-3 * x + 3) * x + 3) * x + 1) / 6; + float w3 = ((( 1 * x + 0) * x + 0) * x + 0) / 6; + *dst++ = 1 + x - w1 / (w0 + w1); + *dst++ = 1 - x + w3 / (w2 + w3); + *dst++ = w0 + w1; + *dst++ = 0; +} Index: mythtv/libs/libmythtv/videoout_xv.cpp =================================================================== --- mythtv/libs/libmythtv/videoout_xv.cpp (revision 17692) +++ mythtv/libs/libmythtv/videoout_xv.cpp (working copy) @@ -120,7 +120,7 @@ xv_colorkey(0), xv_draw_colorkey(false), xv_chroma(0), - gl_context_lock(false), gl_context(NULL), + gl_context_lock(true), gl_context(NULL), gl_videochain(NULL), gl_pipchain(NULL), gl_osdchain(NULL), @@ -215,9 +215,9 @@ needrepaint = true; } - if (gl_videochain) + if (gl_videochain && gl_context) { - QMutexLocker locker(&gl_context_lock); + OpenGLContextLocker ctx_lock(gl_context); gl_videochain->SetVideoRect(display_video_rect, video_rect); } } @@ -918,33 +918,34 @@ #ifdef USING_OPENGL_VIDEO ok = gl_context; - gl_context_lock.lock(); + gl_context_lock.lock(); if (!ok) { - gl_context = new OpenGLContext(); + gl_context = new OpenGLContext(&gl_context_lock); ok = gl_context->Create( XJ_disp, XJ_win, XJ_screen_num, - display_visible_rect.size(), true); + display_visible_rect, + db_use_picture_controls); } + gl_context_lock.unlock(); + if (ok) { + OpenGLContextLocker ctx_lock(gl_context); gl_context->Show(); - gl_context->MakeCurrent(true); gl_videochain = new OpenGLVideo(); ok = gl_videochain->Init(gl_context, db_use_picture_controls, - true, video_dim, - display_visible_rect, - display_video_rect, video_rect, true); - gl_context->MakeCurrent(false); + video_dim, display_visible_rect, + display_video_rect, video_rect, true, + GetFilters()); } - gl_context_lock.unlock(); - if (ok) { + OpenGLContextLocker ctx_lock(gl_context); InstallXErrorHandler(XJ_disp); ok = CreateBuffers(OpenGL); @@ -970,7 +971,6 @@ if (!m_deintfiltername.isEmpty() && !m_deintfiltername.contains("opengl")) { - QMutexLocker locker(&gl_context_lock); gl_videochain->SetSoftwareDeinterlacer(m_deintfiltername); } @@ -1387,19 +1387,18 @@ return xvmc_tex; } - if (osd_renderer == "opengl2") + if (osd_renderer == "opengl2" && gl_context) { - QMutexLocker locker(&gl_context_lock); + OpenGLContextLocker ctx_lock(gl_context); gl_use_osd_opengl2 = true; - gl_context->MakeCurrent(true); - gl_osdchain = new OpenGLVideo(); if (!gl_osdchain->Init( - gl_context, false, true, + gl_context, db_use_picture_controls, GetTotalOSDBounds().size(), GetTotalOSDBounds(), display_visible_rect, - QRect(QPoint(0, 0), GetTotalOSDBounds().size()), false, true)) + QRect(QPoint(0, 0), GetTotalOSDBounds().size()), false, + GetFilters(), true)) { VERBOSE(VB_PLAYBACK, LOC_ERR + "InitOSD(): Failed to create OpenGL2 OSD"); @@ -1411,8 +1410,6 @@ { gl_osdchain->SetMasterViewport(gl_videochain->GetViewPort()); } - - gl_context->MakeCurrent(false); } if (osd_renderer == "chromakey") @@ -1726,12 +1723,14 @@ { (void) enable; - if (!gl_videochain) + if (!gl_videochain || !gl_context) return false; if (enable && m_deinterlacing && (OpenGL != VideoOutputSubType())) return m_deinterlacing; + OpenGLContextLocker ctx_lock(gl_context); + if (enable) { if (m_deintfiltername == "") @@ -1744,9 +1743,7 @@ else if (!m_deintfiltername.contains("opengl")) { // make sure opengl deinterlacing is disabled - gl_context_lock.lock(); gl_videochain->SetDeinterlacing(false); - gl_context_lock.unlock(); if (!m_deintFiltMan || !m_deintFilter) return VideoOutput::SetupDeinterlace(enable); @@ -1754,10 +1751,7 @@ } if (gl_videochain) - { - QMutexLocker locker(&gl_context_lock); gl_videochain->SetDeinterlacing(enable); - } m_deinterlacing = enable; @@ -1770,13 +1764,16 @@ (void) interlaced; (void) overridefilter; + if (!gl_videochain || !gl_context) + return false; + + OpenGLContextLocker ctx_lock(gl_context); + m_deintfiltername = db_vdisp_profile->GetFilteredDeint(overridefilter); if (!m_deintfiltername.contains("opengl")) { - gl_context_lock.lock(); gl_videochain->SetDeinterlacing(false); - gl_context_lock.unlock(); gl_videochain->SetSoftwareDeinterlacer(QString::null); @@ -1806,8 +1803,6 @@ if (!gl_videochain) return false; - QMutexLocker locker(&gl_context_lock); - if (m_deinterlacing && !m_deintfiltername.isEmpty()) { if (gl_videochain->GetDeinterlacer() != m_deintfiltername) @@ -2302,7 +2297,8 @@ #endif // USING_XVMC // OpenGL stuff - gl_context_lock.lock(); + if (gl_context) + gl_context->MakeCurrent(true); if (gl_videochain) { @@ -2319,16 +2315,17 @@ delete gl_osdchain; gl_osdchain = NULL; } -#ifdef USING_OPENGL + if (gl_context) + { gl_context->Hide(); -#endif + gl_context->MakeCurrent(false); + } + gl_use_osd_opengl2 = false; gl_pip_ready = false; gl_osd_ready = false; allowpreviewepg = true; - - gl_context_lock.unlock(); // end OpenGL stuff vbuffers.DeleteBuffers(); @@ -2671,8 +2668,11 @@ { (void) t; - QMutexLocker locker(&gl_context_lock); + if (!gl_videochain || !gl_context) + return; + OpenGLContextLocker ctx_lock(gl_context); + if (!buffer) buffer = vbuffers.GetScratchFrame(); @@ -2682,7 +2682,6 @@ if (buffer->codec != FMT_YV12) return; - gl_context->MakeCurrent(true); gl_videochain->PrepareFrame(t, m_deinterlacing, framesPlayed); if (gl_pip_ready && gl_pipchain) @@ -2691,8 +2690,7 @@ if (gl_osd_ready && gl_osdchain) gl_osdchain->PrepareFrame(t, m_deinterlacing, framesPlayed); - gl_context->Flush(); - gl_context->MakeCurrent(false); + gl_context->Flush(false); if (vbuffers.GetScratchFrame() == buffer) vbuffers.SetLastShownFrameToScratch(); @@ -3060,11 +3058,8 @@ ShowXvMC(scan); else if (VideoOutputSubType() == XVideo) ShowXVideo(scan); - else if (VideoOutputSubType() == OpenGL) - { - QMutexLocker locker(&gl_context_lock); + else if (VideoOutputSubType() == OpenGL && gl_context) gl_context->SwapBuffers(); - } X11S(XSync(XJ_disp, False)); } @@ -3106,9 +3101,10 @@ VERBOSE(VB_PLAYBACK, LOC + "Initialise PiP."); gl_pipchain = new OpenGLVideo(); bool success = gl_pipchain->Init(gl_context, db_use_picture_controls, - true, QSize(pipVideoWidth, pipVideoHeight), + QSize(pipVideoWidth, pipVideoHeight), position, position, - QRect(0, 0, pipVideoWidth, pipVideoHeight), false); + QRect(0, 0, pipVideoWidth, pipVideoHeight), false, + GetFilters()); success &= gl_pipchain->AddDeinterlacer("openglonefield"); gl_pipchain->SetMasterViewport(gl_videochain->GetViewPort()); if (!success) @@ -3124,11 +3120,14 @@ { VERBOSE(VB_PLAYBACK, LOC + "Re-initialise PiP."); - bool success = gl_pipchain->ReInit( - gl_context, db_use_picture_controls, - true, QSize(pipVideoWidth, pipVideoHeight), - position, position, - QRect(0, 0, pipVideoWidth, pipVideoHeight), false); + delete gl_pipchain; + gl_pipchain = new OpenGLVideo(); + bool success = gl_pipchain->Init( + gl_context, db_use_picture_controls, + QSize(pipVideoWidth, pipVideoHeight), + position, position, + QRect(0, 0, pipVideoWidth, pipVideoHeight), false, + GetFilters()); gl_pipchain->SetMasterViewport(gl_videochain->GetViewPort()); if (!success) @@ -3667,8 +3666,11 @@ (void) filterList; (void) pipPlayer; - QMutexLocker locker(&gl_context_lock); + if (!gl_videochain || !gl_context) + return; + OpenGLContextLocker ctx_lock(gl_context); + bool pauseframe = false; if (!frame) { @@ -3677,9 +3679,6 @@ pauseframe = true; } - // disable image processing for offscreen rendering - gl_context->MakeCurrent(true); - if (filterList) filterList->ProcessFrame(frame); @@ -3700,10 +3699,10 @@ m_deintFilter->ProcessFrame(frame); } - if (gl_videochain) - gl_videochain->UpdateInputFrame(frame); + bool soft_bob = m_deinterlacing && (m_deintfiltername == "bobdeint"); - gl_context->MakeCurrent(false); + if (gl_videochain) + gl_videochain->UpdateInputFrame(frame, soft_bob); } void VideoOutputXv::ProcessFrameMem(VideoFrame *frame, OSD *osd, @@ -3784,10 +3783,10 @@ if (!supported_attributes) return -1; - if (VideoOutputSubType() == OpenGL) + if (VideoOutputSubType() == OpenGL && gl_context) { newValue = min(max(newValue, 0), 100); - newValue = gl_videochain->SetPictureAttribute(attribute, newValue); + newValue = gl_context->SetPictureAttribute(attribute, newValue); if (newValue >= 0) SetPictureAttributeDBValue(attribute, newValue); return newValue; @@ -3846,9 +3845,9 @@ { supported_attributes = kPictureAttributeSupported_None; - if (VideoOutputSubType() == OpenGL) + if (VideoOutputSubType() == OpenGL && gl_context) { - supported_attributes = gl_videochain->GetSupportedPictureAttributes(); + supported_attributes = gl_context->GetSupportedPictureAttributes(); } else if (VideoOutputSubType() >= XVideo) { @@ -4183,9 +4182,7 @@ surface->v - surface->yuvbuffer, }; gl_osdchain->UpdateInput(surface->yuvbuffer, offsets, - 0, FMT_YV12, visible); - gl_osdchain->UpdateInput(surface->alpha, offsets, - 3, FMT_ALPHA, visible); + FMT_YV12, visible, surface->alpha); } return changed; } Index: mythtv/libs/libmythtv/videodisplayprofile.cpp =================================================================== --- mythtv/libs/libmythtv/videodisplayprofile.cpp (revision 17692) +++ mythtv/libs/libmythtv/videodisplayprofile.cpp (working copy) @@ -689,6 +689,11 @@ return QObject::tr("Linear blend (2x, HW)"); else if ("opengldoubleratefieldorder" == short_name) return QObject::tr("Interlaced (2x, Hw)"); + else if ("opengldoublerateyadif" == short_name) + return QObject::tr("Yadif (2x, Hw)"); + else if ("openglyadif" == short_name) + return QObject::tr("Yadif (Hw)"); + return ""; } @@ -1245,6 +1250,8 @@ msg = kLinearBlendMsg + " " + kUsingOpenGL; else if (deint == "openglkerneldeint") msg = kKernelMsg + " " + kUsingOpenGL; + else if (deint == "openglyadif") + msg = kYadifMsg + " " + kUsingOpenGL; else if (deint == "opengldoubleratelinearblend") msg = kLinearBlendMsg + " " + kUsingOpenGLWorkaround; else if (deint == "opengldoublerateonefield") @@ -1261,6 +1268,8 @@ msg = kYadifMsg; else if (deint == "yadifdoubleprocessdeint") msg = kYadifMsg + " " + kDoubleRateMsg; + else if (deint == "opengldoublerateyadif") + msg = kYadifMsg + " " + kUsingOpenGLWorkaround; else msg = QObject::tr("'%1' has not been documented yet.").arg(deint); @@ -1435,10 +1444,12 @@ "openglkerneldeint" "openglonefield" "openglbobdeint" +"openglyadif" "opengldoubleratelinearblend" "opengldoublerateonefield" "opengldoubleratekerneldeint" "opengldoubleratefieldorder" +"opengldoublerateyadif" */ void VideoDisplayProfile::init_statics(void) @@ -1488,13 +1499,14 @@ safe_deint["opengl"] += "opengllinearblend"; safe_deint["opengl"] += "openglonefield"; safe_deint["opengl"] += "openglkerneldeint"; - safe_deint["opengl"] += "bobdeint"; safe_deint["opengl"] += "openglbobdeint"; safe_deint["opengl"] += "opengldoubleratelinearblend"; safe_deint["opengl"] += "opengldoublerateonefield"; safe_deint["opengl"] += "opengldoubleratekerneldeint"; safe_deint["opengl"] += "opengldoubleratefieldorder"; + safe_deint["opengl"] += "opengldoublerateyadif"; + safe_deint["opengl"] += "openglyadif"; safe_osd["xv-blit"] += "softblend"; safe_osd["xvmc-blit"] += "chromakey";