From d58b6b908ea130e9422b08b5b3b460b48da7d337 Mon Sep 17 00:00:00 2001 From: Tomasz Olszak Date: Tue, 13 Jun 2017 11:24:47 +0200 Subject: Improve quality of YUVY and UYVY rendering Idea is to upload YUYV/UYVY data as 2 textures and use GL_LINEAR like in biplanar formats. Having proper interpolation of only one texture (using e.g. mix function) resulted in vertical and horizontal 1 pixel stripes depending on scale. The reason was float precision and unexpected value of fract function. Additionally branching in shader is expensive so this solution should be more performant. Task-number: QTBUG-62155 Change-Id: I7ceeb09b4a54eecd16640a626b499d638b52c127 Reviewed-by: Christian Stromme --- src/qtmultimediaquicktools/qsgvideonode_yuv.cpp | 32 ++++++++++++++--------- src/qtmultimediaquicktools/shaders/uyvyvideo.frag | 18 +++---------- src/qtmultimediaquicktools/shaders/yuyvvideo.frag | 18 +++---------- 3 files changed, 27 insertions(+), 41 deletions(-) diff --git a/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp b/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp index 38b5af943..b04c6b38b 100644 --- a/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp +++ b/src/qtmultimediaquicktools/qsgvideonode_yuv.cpp @@ -134,16 +134,16 @@ public: protected: void initialize() Q_DECL_OVERRIDE { m_id_matrix = program()->uniformLocation("qt_Matrix"); - m_id_yuvtexture = program()->uniformLocation("yuvTexture"); - m_id_imageWidth = program()->uniformLocation("imageWidth"); + m_id_yTexture = program()->uniformLocation("yTexture"); + m_id_uvTexture = program()->uniformLocation("uvTexture"); m_id_colorMatrix = program()->uniformLocation("colorMatrix"); m_id_opacity = program()->uniformLocation("opacity"); QSGMaterialShader::initialize(); } int m_id_matrix; - int m_id_yuvtexture; - int m_id_imageWidth; + int m_id_yTexture; + int m_id_uvTexture; int m_id_colorMatrix; int m_id_opacity; }; @@ -291,7 +291,7 @@ QSGVideoMaterial_YUV::QSGVideoMaterial_YUV(const QVideoSurfaceFormat &format) : case QVideoFrame::Format_UYVY: case QVideoFrame::Format_YUYV: default: - m_planeCount = 1; + m_planeCount = 2; break; } @@ -355,12 +355,19 @@ void QSGVideoMaterial_YUV::bind() if (m_format.pixelFormat() == QVideoFrame::Format_UYVY || m_format.pixelFormat() == QVideoFrame::Format_YUYV) { - int fw = m_frame.width() / 2; + int fw = m_frame.width(); m_planeWidth[0] = fw; - - functions->glActiveTexture(GL_TEXTURE0); - bindTexture(m_textureIds[0], fw, m_frame.height(), m_frame.bits(), GL_RGBA); - + // In YUYV texture the UV plane appears with the 1/2 of image and Y width. + m_planeWidth[1] = fw / 2; + functions->glActiveTexture(GL_TEXTURE1); + // Either r,b (YUYV) or g,a (UYVY) values are used as source of YV. + // Additionally Y and V are set per 2 pixels hence only 1/2 of image with is used. + // Interpreting this properly in shaders allows to not copy or not make conditionals inside shaders, + // only interpretation of data changes. + bindTexture(m_textureIds[1], m_planeWidth[1], m_frame.height(), m_frame.bits(), GL_RGBA); + functions->glActiveTexture(GL_TEXTURE0); // Finish with 0 as default texture unit + // Either red (YUYV) or alpha (UYVY) values are used as source of Y + bindTexture(m_textureIds[0], m_planeWidth[0], m_frame.height(), m_frame.bits(), GL_LUMINANCE_ALPHA); } else if (m_format.pixelFormat() == QVideoFrame::Format_NV12 || m_format.pixelFormat() == QVideoFrame::Format_NV21) { const int y = 0; @@ -473,13 +480,12 @@ void QSGVideoMaterialShader_UYVY::updateState(const RenderState &state, Q_UNUSED(oldMaterial); QSGVideoMaterial_YUV *mat = static_cast(newMaterial); - - program()->setUniformValue(m_id_yuvtexture, 0); + program()->setUniformValue(m_id_yTexture, 0); + program()->setUniformValue(m_id_uvTexture, 1); mat->bind(); program()->setUniformValue(m_id_colorMatrix, mat->m_colorMatrix); - program()->setUniformValue(m_id_imageWidth, mat->m_frame.width()); if (state.isOpacityDirty()) { mat->m_opacity = state.opacity(); diff --git a/src/qtmultimediaquicktools/shaders/uyvyvideo.frag b/src/qtmultimediaquicktools/shaders/uyvyvideo.frag index 905035703..5c62441c2 100644 --- a/src/qtmultimediaquicktools/shaders/uyvyvideo.frag +++ b/src/qtmultimediaquicktools/shaders/uyvyvideo.frag @@ -1,22 +1,12 @@ -uniform sampler2D yuvTexture; // UYVY macropixel texture passed as RGBA format -uniform mediump float imageWidth; // The UYVY texture appears to the shader with 1/2 the image width since we use the RGBA format to pass UYVY +uniform sampler2D yTexture; // Y component passed as GL_LUMINANCE_ALPHA, in uyvy Y = a +uniform sampler2D uvTexture; // UV component passed as RGBA macropixel, in uyvy U = r, V = b uniform mediump mat4 colorMatrix; uniform lowp float opacity; - varying highp vec2 qt_TexCoord; void main() { - // For U0 Y0 V0 Y1 macropixel, lookup Y0 or Y1 based on whether - // the original texture x coord is even or odd. - mediump float Y; - if (fract(floor(qt_TexCoord.x * imageWidth + 0.5) / 2.0) > 0.0) - Y = texture2D(yuvTexture, qt_TexCoord).a; // odd so choose Y1 - else - Y = texture2D(yuvTexture, qt_TexCoord).g; // even so choose Y0 - mediump float Cb = texture2D(yuvTexture, qt_TexCoord).r; - mediump float Cr = texture2D(yuvTexture, qt_TexCoord).b; + mediump vec3 YUV = vec3(texture2D(yTexture, qt_TexCoord).a, texture2D(uvTexture, qt_TexCoord).rb); - mediump vec4 color = vec4(Y, Cb, Cr, 1.0); - gl_FragColor = colorMatrix * color * opacity; + gl_FragColor = colorMatrix * vec4(YUV, 1.0) * opacity; } diff --git a/src/qtmultimediaquicktools/shaders/yuyvvideo.frag b/src/qtmultimediaquicktools/shaders/yuyvvideo.frag index 72732cd66..5ce8b7366 100644 --- a/src/qtmultimediaquicktools/shaders/yuyvvideo.frag +++ b/src/qtmultimediaquicktools/shaders/yuyvvideo.frag @@ -1,22 +1,12 @@ -uniform sampler2D yuvTexture; // YUYV macropixel texture passed as RGBA format -uniform mediump float imageWidth; // The YUYV texture appears to the shader with 1/2 the image width since we use the RGBA format to pass YUYV +uniform sampler2D yTexture; // Y component passed as GL_LUMINANCE_ALPHA, in yuyv Y = r +uniform sampler2D uvTexture; // UV component passed as RGBA macropixel, in uyvy U = g, V = a uniform mediump mat4 colorMatrix; uniform lowp float opacity; - varying highp vec2 qt_TexCoord; void main() { - // For Y0 U0 Y1 V0 macropixel, lookup Y0 or Y1 based on whether - // the original texture x coord is even or odd. - mediump float Y; - if (fract(floor(qt_TexCoord.x * imageWidth + 0.5) / 2.0) > 0.0) - Y = texture2D(yuvTexture, qt_TexCoord).b; // odd so choose Y1 - else - Y = texture2D(yuvTexture, qt_TexCoord).r; // even so choose Y0 - mediump float Cb = texture2D(yuvTexture, qt_TexCoord).g; - mediump float Cr = texture2D(yuvTexture, qt_TexCoord).a; + mediump vec3 YUV = vec3(texture2D(yTexture, qt_TexCoord).r, texture2D(uvTexture, qt_TexCoord).ga); - mediump vec4 color = vec4(Y, Cb, Cr, 1.0); - gl_FragColor = colorMatrix * color * opacity; + gl_FragColor = colorMatrix * vec4(YUV, 1.0) * opacity; } -- cgit v1.2.1