我目前正在尝试将简单的滤镜效果应用于HTML5 Canvas上的图像。与此处定义的技术类似:
HTML5 Canvas image contrast 并在此HTML5 Rocks! blog post
然而,这种特殊方法需要迭代每个像素并在重绘之前应用修改器。对于我的特定用例,这需要150ms +来重绘我的图像(650px到650px PNG)
使用CSS3's filter property(对比度或亮度)应用相同效果的时间不到10毫秒。
我的问题是: CSS3&#s;过滤属性如何工作"引擎盖"?为什么它的性能要高得多?有没有办法在Canvas中实现类似的性能?
答案 0 :(得分:3)
Canvas通过API公开。 JavaScript是一个运行时,即使现在经过严格优化,如果您要通过JavaScript逐个像素地访问操作2D网格,您将从运行时一直到缓冲区支付所述访问的代价。包含当前与画布一起存储的图像,用于每个像素。问题必然在于您使用JavaScript表达过滤器代码,即使经过优化,运行时的性质也会导致一些无法取消的惩罚。
CSS3过滤器是一个黑盒子,可以批量执行相同的转换 ,这意味着例如GPU着色器程序直接在GPU上运行,访问图像通常存储在靠近GPU的紧邻的可寻址存储器中,而设计的后者benefiting from its SIMD-class instructions 用于处理整个像素矩阵。此外,本机代码访问本地内存 - 几乎与它一样快,而不需要了解详细信息。
即使在CPU上运行,没有任何GPU辅助,我们也在谈论本机过滤器内核直接操作RAM中的图像,没有任何字节码,更重要的是,根本不考虑JavaScript。您声明了所需的过滤器,用户代理调用画布图像上的过滤器程序,就是这样。 CPU也有SIMD指令来处理数据向量,这显然有很大帮助。过滤器代码甚至不是您的,您只能按名称引用它。
现在,如果您可以将CSS中可用的某种黑盒过滤器直接应用到画布像素数据,您可能会获得与CSS相同的速度 - 因为消除了最快的速度障碍 - 用JavaScript代码表示的逐像素访问。所以这不仅仅是关于JavaScript,而是关于数据访问的粒度。在这种情况下,将内核应用于批量数据总是比在更高级别上自己编写内核代码更快。简单来说,这让我想到了下面的最后一点。
现在,如果JavaScript解释器可以理解你的逐个像素的循环操作,因为它可以将它全部转换为使用SIMD的本机代码,甚至可能{ {3}},全部来自您自由输入的JavaScript过滤器代码,可以弥补性能差距。但是你会把复杂性转移到JavaScript编译器/解释器中,并且在这种规模上优化的问题在计算机科学中还不是完全解决的问题。也许人工智能和机器学习会有所帮助,我不会推测。请注意,我不是在说GPU shaders您的JavaScript与等效的本机代码,这已经成为多年来的常态,我在谈论识别自由类型的JavaScript代码,类似于已知的代码或者甚至是任意图像内核,就像人类一样。然后用运行时对应替换所述代码,该对应程序提供相同的结果,但由编译器编写,以产生它认为将提供最佳性能的内容。
在实践中,如果您深入了解JIT-compiling,我认为您可以优化基于Canvas的JavaScript幼稚的逐像素过滤器。可以通过Uint8ClampedArray
类型访问图像数据,您可以从CanvasRenderingContext2D.getImageData()
方法调用获取该类型。这个数组类型有一些有趣的“批量”函数,如filter
,forEach
,map
,reduce
等。在浏览时,你需要考虑一下“黑客” Canvas API文档,假设Canvas API人的心态 - 根据他们可以提供的内容查看可用的方法和数据类型。这样做的回报可能很大。
如果这还不够,可以使用demoscene渲染画布,WebGL是OpenGL的子集,通常实现为仅在GPU上运行。着色器保证成为WebGL的一部分,这意味着您可以通过WebGL获得各种高级和超高速画布过滤器程序的免费门票,这些程序是您自己编写但通常在GPU上执行的程序。