【问题标题】:why is YUV420 to RGB conversion on iMX53 slow?为什么 iMX53 上 YUV420 到 RGB 的转换很慢?
【发布时间】:2014-04-04 15:47:55
【问题描述】:

我有一个工作链,iMX53 上的 GPU (AMD Z430) 采用 YUV420P 格式的解码视频帧,转换为 RGB565 并显示它。我唯一关心的是速度,更确切地说是缺乏速度。输入视频帧为1920x1088 YUV420P,转换时间为40ms,我根本无法让它跑得更快。我试图优化我的着色器,但没有运气。我也尝试过 2D 色域,它甚至更慢(并且由于其 2D 特性,它提供了一些不正确的颜色)。当然,我不是 OpenGL ES 专家。

这是我的着色器:

static const char *fragment_shader_yuv_src =
    "const lowp mat3 rgb_coeff = mat3(1, 1, 1, 0, -0.344, 1.772, 1.402, -0.714, 0);\n"
    "varying lowp vec2 v_texcoord;\n"
    "uniform lowp sampler2D s_texture_y;\n"
    "uniform lowp sampler2D s_texture_u;\n"
    "uniform lowp sampler2D s_texture_v;\n"
    "\n"
    "void main()\n"
    "{\n"
    "    lowp vec3 yuv = vec3(texture2D(s_texture_y, v_texcoord).r, texture2D(s_texture_u, v_texcoord).r - 0.5, texture2D(s_texture_v, v_texcoord).r - 0.5);\n"
    "    gl_FragColor = vec4(rgb_coeff * yuv, 1.0);\n"
    "}\n";

static const char *vertex_shader_yuv_src =
    "attribute lowp vec4 position; \n"
    "attribute lowp vec2 texcoord; \n"
    "varying lowp vec2 v_texcoord; \n"
    "                              \n"
    "void main()                   \n"
    "{                             \n"
    "    gl_Position = position;   \n"
    "    v_texcoord = texcoord.xy; \n"
    "}                             \n";

s_texture_y/u/v 包含适当的颜色分量,图像由 eglCreateImageKHR(...) 分配,并由 glEGLImageTargetTexture2DOES(...) 分配给纹理。

正如我上面提到的,它可以工作,但速度很慢。我无法确定这是 GPU 的最大性能还是我在着色器中做错了什么......

(使用最简单的着色器放大和绘制简单的 416x416 RGBA32 图像也很慢,~23ms)

任何人有任何想法,经验?我应该如何优化我的着色器?

【问题讨论】:

    标签: opengl-es shader rgb yuv


    【解决方案1】:

    乍一看,您的着色器在性能方面看起来还不错。 (作为参考,请查看 gst-plugins-gl 的 glupload.c 文件,其中典型的转换是在着色器中完成的,适用于 OpenGL 和 OpenGL ES)。

    在不涉及平台细节的情况下,性能不足可能完全是由于上传到 GPU 的损失。您是否尝试过使用虚拟片段着色器?即:

    void main (void)
    {
       gl_FragColor = vec4 (0.0, 1.0, 0.0, 1.0);
    }
    

    在上传不同分辨率的帧时?我的猜测是,即使对于微小的帧,您也会遭受大约 10,20 毫秒的巨大时间损失,这是因为需要完成所有正在进行的计算并在注入新数据之前刷新管道(即上传是从 GPU 的角度来看同步操作)。

    如果损失较大,您可以尝试使用双上传到 GPU(请参阅this post),看看它是否会改善您的特定驱动程序+硬件。

    最后,在您的着色器和 glupload.c 中,使用 3 种不同的纹理,每个通道一个,并且使用从 CPU 代码传递的制服不会提高性能。您可以尝试两件事:第一,使用三个通道 YUV 背靠背上传一组数据,然后将 output-pixel-to-yuv-coordinate 逻辑引入着色器本身,如下所示:

    void main (void)
    { 
       vec2 y_coord = <calculate the position to find the Y component for this pixel>
       vec2 u_coord = <calculate the position to find the U component for this pixel>
       vec2 v_coord = <calculate the position to find the V component for this pixel>
       float y_value = texture2D(s_texture, y_coord).<appropriate channel>
       float u_value = texture2D(s_texture, u_coord).<appropriate channel>
       float v_value = texture2D(s_texture, v_coord).<appropriate channel>
       gl_FragColor = vec4 (rgb_coeff * vec3(y_value, u_value, v_value), 1.0);
    }
    

    如果上传不是瓶颈,我可以尝试进一步开发此着色器...

    【讨论】:

    • 感谢您的回答。我已经使用虚拟片段着色器进行了尝试,1920x1088 的渲染时间约为 17 毫秒。很慢。我担心内存带宽不够……在很多情况下,我在 iMX53 上看到过类似的问题。 GPU 根本无法从支持 DMA 的内存中以足够快的速度复制数据(在这台机器上没有缓冲和缓存,因此非常慢)。顺便说一句,我没有在这台机器上使用 glTexImage2D,因为速度很慢......相反,纹理存储在 eglCreateImageKHR 图像中。
    • 那么双纹理解决方案呢:分配两个纹理,一个用于转换,另一个用于上传?
    • 好吧,视频帧现在直接解码为 eglCreateImageKHR 分配的图像,所以我根本不执行 memcpy(...) 或任何类似操作。 GPU 必须将这些字节复制到它的内存(或其他)中,我相信我无法保存...
    • 在检查了 iMX6Solo 之后,在我看来,iMX53 中的 GPU 根本不足以以我需要的速度执行这项任务。 iMX6Solo 的 Vivante GPU 至少快 2.5 倍,并且支持原生 YUV 纹理,因此无需使用自定义着色器将 YUV 转换为 RGB。
    • 我不知道堆栈溢出礼仪是否允许这样做,但在我看来,Clay Montgomery 可能是最有资格在飞思卡尔 GPU 上离题的人......
    猜你喜欢
    • 2020-01-03
    • 2022-12-22
    • 2020-12-18
    • 2011-09-27
    • 2014-05-16
    • 2021-04-13
    • 2019-11-12
    • 1970-01-01
    • 2016-02-06
    相关资源
    最近更新 更多