查看: 418|回复: 0

iOS Video Toolbox:GPGPU加快YUV图像处置

[复制链接]
发表于 2016-9-30 08:06:30 |未经授权,严禁转载,违者必究... | |阅读模式
处置 图像 加快 Video iOS video ios html5 video ios video player ios video标签 ios video 自动全屏 video ios ios h5 video ios reverse video ios videotoolbox
本文档以调整YUV亮度为例,描述了OpenGL ES 3.0 Transform Feedback(变换反馈)在Video Toolbox解码回调中进行YUV图像处置的具体实现,同时比力多种绘制模式之间的机能差别,此文档方针是操纵GPU并行计较能力,做通用计较(好比浮点计较),便是当成GPGPU利用,减轻非游戏及GPU密集型应用的CPU压力,从而导出一个通用计较骨架用于各类有计较机能需要的场所,是文档

Xcode 8 GPU调试新增功能:查看缓冲区数据巨细
1、同步解码与CPU实现YUV亮度减半
为比力GPU机能,先给出CPU同步解码且在解码回调函数中实现调整YUV亮度操作,朴实实现如代码段1所示,代码中的除法操作只是出于演示目标,整除2换成移位操作按理会对机能有所改善,可是,假如除以非2的幂则无法利用移位操作了,感激@Bepartofyou指出此示例的不恰当问题。我原本是想比力CPU做浮点操作与GPU的机能差别,只是刚巧Y通道是8位整数值

在iPhone 5(iOS 9.2)上运行了2055.8秒,平均每帧耗损0.47秒
在iPhone 6p(iOS 9.3.5)上运行了95.7秒,平均每帧耗损0.02秒,可见两年后的设备机能确实是年夜幅晋升
iPad Air 2与iPhone 6p表示接近。
再对比iPhone 7的表示,相对iPad Air 2提高了10秒摆布
因iOS平台限制,Transform Feedback必需利用GLKit,故解码回调函数的实现代码变得略微复杂
// 代码段2-1:通知主线程
dispatch_sync(dispatch_get_main_queue(), ^{
    // 通知主线程绘制
});
// 代码段2-2:Y通道建立纹理
- (void)generateTextureFromCVPixelBuffer:(CVPixelBufferRef)pixelBuffer {
    [lock lock];
    isNewPixelBuffer = true;
    CVOpenGLESTextureCacheFlush(textureCache, 0);
    if (texture) {
        CFRelease(texture);
    }
    int imageWidth = (int)CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
    int imageHeight = (int)CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
    CVReturn createResult = CVOpenGLESTextureCacheCreateTextureFromImage(NULL,
        textureCache,
        pixelBuffer,
        NULL,
        GL_TEXTURE_2D,
        GL_LUMINANCE,
        imageWidth, imageHeight,
        GL_LUMINANCE,
        GL_UNSIGNED_BYTE,
        0,
        &texture);
    if (kCVReturnSuccess != createResult) {
        NSLog(@"CVOpenGLESTextureCacheCreateTextureFromImage failed.");
    }
    glBindTexture(CVOpenGLESTextureGetTarget(texture),
        CVOpenGLESTextureGetName(texture));
    glTexParameteri(GL_TEXTURE_2D,
        GL_TEXTURE_WRAP_S,
        GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,
        GL_TEXTURE_WRAP_T,
        GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D,
        GL_TEXTURE_MIN_FILTER,
        GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D,
        GL_TEXTURE_MAG_FILTER,
        GL_NEAREST);
    [lock unlock];
}
// 代码段2-3:GPU处置Y通道数据
// 进行变换反馈,CPU期待GPU运行竣事,获取GPU内存且进行跨距处置
GLuint *addr = glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER,
    0,
    pointsCount * sizeof(GLuint),
    GL_MAP_READ_BIT);
GLuint *pAddr = addr;
if (addr) {
    uint8_t *luma = calloc(pointsCount, sizeof(uint8_t)), *pLuma = luma;
    for (int line = 0; line
此刻,真机运行查看机能表示

可见,CPU压力几乎全在主线程,但负荷较低,其余焦点几乎处于休眠状况,主线程多次呈现尖峰可能由建立并上传纹理的代码引起,后续文档会对此进行优化下面不雅察GPU的资本占用。
苹果利用PowerVR的图形处置器,而PowerVR处置极点是基于Tile(贴片)体例实现的,那么上图反映了我们代码确实只有极点着色器代码在工作,因为PowerVR处置片段着色器对应于RENDER功能。DEVICE图表则暗示了GPU整体利用,包含极点与片段着色器的代码耗损。有关Utilization图表的申明如下所示。
The Renderer bar measures use of the GPU’s pixel processing resources. High renderer utilization can indicate performance bottlenecks in the fragment and pixel processing stages of the OpenGL ES pipeline, such as using inefficient fragment shader code or processing additional fragments each frame for color blending.
有关GPU相关东西申明可官方文档

1613657-e5e9cd351a01bb00.jpg

1613657-e5e9cd351a01bb00.jpg

WWDC对A7处置器的申明(基于贴片延迟衬着手艺)
固然当前代码占用了较多CPU和GPU时候,从CPU焦点利用率上看,至少,我们的初步方针实现了:在非游戏和图形密集计较的应用中,利用GPU计较、释放CPU压力。接下来,进行机能测试,确定此体例是否达到现实产物需求并进行机能瓶颈定位
利用上一节生成的1920x1080个点坐标,只点窜绘制参数,从绘点GL_POINTS到画线GL_LINES后,帧率提高了一倍,达到每秒6帧,时候减半,不外,CPU占用略有提高,平均为9%从这一表示,可揣度,将绘制体例改成三角形格(Mesh),那么,帧率将继续提高
因为点坐标没从头生成,意味着,(0, 0)(0, 1)连成线、(0, 2)(0, 3)连成线,而(0, 1)(0, 2)两点则未结成线,以此类推。然而,绘制的成果画面是完整的,参考退化三角形的概念,我的理解是,GPU可能拒绝了这种绘制直线的体例,从而并没画线,只是一次读取取两个端点,加速了极点读取速度,然后只是简单地画成点阵。之前我按照印象,说可能形成『退化直线』,即长度0,因为在线衬着(Live Rendering,最终成果输出到默认帧缓冲区)的场所,光栅化的成果中最小绘制单元是像素点,而此处两个点之间再无间隙可供插值或填充。这种理解在从头查阅资料后,发现是错误的。退化三角形的界说是判定极点是否重合,而非三角形面积是否为0,完整界说如下所示
本节验证了上一节同步解码与GPU逐行绘制实现YUV亮度减半的推论。利用第一节生成的点阵绘制自力三角形,帧率从6升到8帧每秒,比预想值少了一帧,总耗时及每帧平均耗损的CPU与GPU时候有所改善,然而,CPU占用率从平均9%涨到平均12%,具体资本占用信息如下
在上一节绘制自力三角形的根本上,利用OpenGL ES 3.0新增特征实例化衬着(Instancing Rendering)实现1920x1080点阵处置,查看Instancing Rendering是否合用GPGPU场所。其实,在极点着色器中做纹理采样也是ES 3.0的新特征Instancing Rendering的特点是一次画图挪用可绘制多个相似的物体,而且这些物体可以拥有分歧属性,好比位置、颜色、标的目的等。在绘制人群、丛林等存在年夜量相似物体的场景,它可避免多次glDraw*挪用,节流CPU时钟周期具体应用示例如下图所示,图是偷WWDC的,我认可
因实例化衬着(Instancing Rendering)存在两种用法,故下面分两末节进行描述
按照WWDC的介绍,对于片段着色器而言,点窜纹理坐标长短常影响机能的。揣度,对于极点坐标也是如斯为了适配新的绘制方式,还需利用glDrawArraysInstanced替代glDrawArrays,这个函数的界说如下所示
前三个参数与glDrawArrays的界说不异,最后一个参数primcount注解要绘制的物体数目。故替代旧绘制函数为:
此刻,只需一行的点阵数据就可处置1920x1080的图像了,节约了年夜量存储空间。对比本文最先的截图,Array Buffer和TF Buffer别离从15.8MB、15.8MB削减到15KB和7.9MB。GPU帧率从7提高到8,不外,CPU和GPU耗时根基没改善
基于glVertexAttribDivisor函数的实现需要的代码略多些,之所以需要两种体例都实现,是因为WWDC讲座建议我们进行真机实测,选择机能更好的实现体例。此刻调整代码以适配此实现体例。
从下面运行截图看,glVertexAttribDivisor实现体例的GPU缓冲区占用环境与gl_InstancingID一样。帧率在7和8之间转变,大都环境在7帧率,CPU耗时根基一致
经测试,glDrawElements在iOS上无法共同变换反馈至此,能顺遂进行变换反馈、编程最简单且机能最好的画图模式就是GL_TRIANGLES。我认为GL_TRIANGLES只是提高了极点坐标的消费速度。不考虑可否共同变换反馈,纯真地比力机能,我感觉glDrawArrays固然需要占用更多极点坐标存储空间,可是,它的机能可能比glDrawElements高,类比CPU的工作体例,极点是线性存储和读取,可以预加载接下来要绘制的极点坐标,不会呈现GPU预读取缓存掉效问题。而glDrawElements利用索引只是削减极点坐标数目,节流了占用的显存空间,因为索引指向的值可对应到存储位置分离的极点,如斯一来,每次可能需要随机读取,降低了预读取数据的射中率,影响绘制机能。是以,才推出图元重启和多重绘制(glMultiDrawElements)等接口,经由过程一次画图挪用实现多个分离图形的绘制来改善它们的机能
因为GPU处置完的数据为4字节,而Y通道的数据类型是单字节,映射GPU内存后需进行跨距内存拷贝,朴实实现代码对此插手同步操作,引起气鼓鼓泡效应,CPU相关机能耗损如下图所示
将绘制代码从GLKit的回调函数移到生成纹理的函数后,即,简化了主线程加锁过程及判定按照新纹理进行绘制的代码,运行新代码,发现即使主线程的CPU占用率还处于平均12%的程度,不外,计较每帧图像的CPU耗时降低至1毫秒摆布,新的运行状况如下所示
对比iPhone 7的表示,可见GPU机能年夜幅晋升。节制台输出的Metal信息以及Edit Scheme...面板上的Profile上的转变,加上2016年WWDC视频WWDC: 602 Adopting Metal Part 1
实现上述优化的依据是,按照OpenGL ES号令执行的特点,固然号令提交到EGL上下文后是并行执行,但总体上遵守先提交先执行(因为是号令队列)的原则,是以可以移除CPU期待GPU完成的代码,此时进行机能测试发现,独一一次CPU期待GPU号令完成是系统的操作,我临时没想到绕过的法子
。简化glFinish等CPU同步GPU代码后,引入了一个新问题:GPU Wait on Buffer
按照
[exxfilelink]1[/exxfilelink]
glDiscardFramebufferEXT与glInvalidateFramebuffer
6.5、逻辑缓冲区加载
Your application caused OpenGL ES to perform a framebuffer load operation, where the framebuffer must be loaded by the GPU before rendering. This is typically caused by failing to perform a fullscreen clear operation at the beginning of each frame. If possible, you should do so to improve performance.
glBindFramebuffer(GL_FRAMEBUFFER, 1u)
A GL function call that sets a piece of GL state to its current value has been detected. Minimize the number of these redundant state calls, since these calls are performing unnecessary work.
利用GLKViewController时,这是框架的挪用,与用户无关,是以我们点窜不了,这点从栈帧信息上也可验证
是以,就像素级此外图像处置而言,从简化编程复杂度出发,我们应该利用光栅化,让OpenGL ES衬着管线帮我们进行每个像素点的计较。当然,为了让每个像素点都介入计较,需包管片段着色器与纹理一一对应,每个片段对应到一个像素,因为凡是环境下,每个片段纷歧定对应到一个像素,可能在裁剪等固定管线功能后,部门像素点位于视景体之外,从而被GPU忽略,省去不需要的计较。这对于图像处置而言是不存在的,因为我们往往将数据写到源纹理一样巨细的离线纹理或衬着缓冲区中
All FP shader calculations performed with scalar processor
3、关于图元重启(Primitive Restart)与变换反馈共同利用的测验考试
[exxfilelink]2[/exxfilelink]
Transform Feedback与glDrawElements一路利用呈现无效操作错误
可是,经查阅资料,有人在gamedev.net会商的Transform feedback and glDrawElements
然而,我的varyings并没转变,着色器也没变,只加了索引数组和绘制模式,而Xcode则提醒无法同时利用
。是以,这个答复应该只是臆想,当然也可能是OpenGL与OpenGL ES限制分歧。可是,
[exxfilelink]3[/exxfilelink]
OpenGL ES在iOS 10由Metal接口封装实现
6、iOS是否利用同一内存模子(Uniform Memory Model)
之前看老外的博客说iOS利用同一内存模子,CPU与GPU共享统一内存空间,在CPU和GPU之间拷贝数据速度很是快
。可是,从WWDC: Advances in OpenGL ES 3.0
7、Instancing Rendering是否比一次上传所有点阵并利用glDrawArrays快
在绘制年夜量相似物体时,多次挪用glDraw函数会占用较多CPU时钟周期,因为glDraw系列函数是CPU请求GPU工作,有彼此通信的过程,这也是很多游戏存在的CPU瓶颈
。WWDC: Advances in OpenGL ES 3.0中说起到,绘制多个相似物体时,假如不利用(Instancing Rendering)实例化绘制,只用glDraw*轻易挤爆OpenGL的号令缓冲区
其实,实例化绘制在OpenGL ES 2.0中iOS已供给响应拓展,在ES 3.0中被收录为焦点功能
称谢
感激同事@落雨天不撑阳伞(产自老和山职业手艺学院)供给三角形网格的新思绪。感激同事@fatzhou(产自五道口男人职业手艺学院)供给对绘制上述三角形网格的优化思绪。固然还没利用他们的新思绪,可是在向两位就教时,本身总结出一个能成功获得数据的结论,也是有收成的。别的,在看WWDC: Advances in OpenGL ES 3.0
视频时,incur一词由另一位同事在毫无上下文帮忙的环境下一次就听出来。至此,鄙人已将膝盖献给了三位同事
感激@平平平淡才是真年夜年夜回首了我的渣渣实现代码。
感激@hhhh年夜年夜给的Flash上实现图像处置的资料,比来加班,来不及看
参考与保举阅读
  • WWDC: Advances in OpenGL ES 3.0
  • WWDC Session 416 - Tools for Tuning OpenGL ES Apps on iOS
  • WWDC: 602 Adopting Metal Part 1
  • Primitive Restart Makes GPGPU Tech Sparkle
  • Transform feedback and glDrawElements
  • Transform Feedback Buffer /w glDrawElements
  • OpenGL ES 3.0编程指南(原书第二版),原版:OpenGL ES 3.0 Programming Guide ( Second Edition )

  • Using Transform Feedback with glDrawElements? 提问者后来弥补,进入变换反馈模式与glDrawElements分歧也能工作,可是没给出DEMO代码,不知是不是嘴炮

  • 返回顶部快速回复上一主题下一主题返回列表

    声远论坛|联系电话:0537-2311005|Archiver|手机版|小黑屋|Sitemap|声远网 |网站地图|网站地图

    鲁公网安备 37089702000485号 | 鲁ICP备 18028751号 | 互联网药品信息服务资格证:(鲁)-经营性-2022-0209 | 增值电信业务经营许可证:鲁B2-20230761号 | (鲁)职介证字[223]:第08120014号

    中国互联网违法和不良信息举报中心 | 山东省互联网违法和不良信息举报中心 | 人工智能生成合成内容标识办法

    GMT+8, 2026-5-24 04:17, Processed in 0.121932 second(s), 32 queries , Gzip On, APCu On.

    Powered by Discuz! X3.5© 2001-2026 SYUAN.COM

    快速回复 返回顶部 返回列表