Pixi.js: 考虑gl矩阵?

创建于 2017-06-30  ·  39评论  ·  资料来源: pixijs/pixi.js

这可能是异端邪说,但我会在这里抛出一个想法,让人们尖叫血腥谋杀......我们有没有考虑过对矩阵进行标准化的可能性,基于数组而不是基于对象的点? WebGL 期望在它的大部分顶点/纹理/等 api 中使用数组。 还有许多现有的生态系统模块在普通的旧数组或本机数组上标准化(例如,https://github.com/toji/gl-matrix) gl-matrix 会很好,因为许多浏览器在标志后面都有硬件 SIMD 支持, 并将很快可用(实际上,gl-matrix 现在内部支持这一点。)

我意识到这对 pixi 来说将是一个巨大的范式转变,并且完全打破了向后兼容性......仍然想把它扔到那里看看人们的想法,以及他们有多少/很少讨厌这个想法;)

最有用的评论

@GoodBoyDigital我想也许你是对的。 求助于我的朋友夫人。 谷歌,我偶然发现了这个:

https://stackoverflow.com/questions/15823021/when-to-use-float32array-instead-of-array-in-javascript

评分最高的答案(目前有 44 票)似乎合理且相关。

所有39条评论

1) SIMD 已死。 https://github.com/rwaldron/tc39-notes/blob/a66df6740eec3358d5e24f81817db99d6ee41401/es8/2017-03/mar-21.md#10if -simdjs-status-update

2) 简单的 6 个字段 "a,b,c,d,tx,ty" 的性能甚至比 Float32Array(9) 矩阵要好得多。 无法提供测试链接,但我和@GoodBoyDigital 都已尝试将其集成。

3) JS 做双精度,这对于使用大坐标的应用程序至关重要。 图形和精灵的“投影 x 变换”最好在 JS 方面。

我在 v5 中使用了 Float32Array(9),在我的 jsperf 测试中,如果不是相同的性能,它是相似的,并且防止我们需要执行 toArray 和转置操作。

https://github.com/pixijs/pixi.js/blob/455c059e8d155c1d9a05fc2ece2c02b3bdf8bf84/plugins/core/src/math/Matrix.js

gl-matrix是有益的,因为它有 SIMD(正如 Ivan 提到的那样是一个死规范),但在它们的实现中也有缺陷。 我们想要一个 3x3 矩阵 ( Float32Array(9) ) 来击中 GPU,但是像 2D 矩阵一样执行操作以节省计算时间。 gl-matrix没有一个很好的机制。

v5 版本使用我们可以直接上传到 GPU 的存储,并且只对我们关心的 2D 部分进行操作。 它还使我们能够使用 SharedArrayBuffers 和其他可能让我们在 webworkers 上投入更多工作的优化。 我们将看看我们能走多远。

@englercj恐怕我们必须在很多地方使用 Math.fround 才能保持一致。 试试 perf Float64Array。

我可以尝试将其设为 Float64Array,但是我们仍然需要将其缩小为单精度以进行上传。 还要记住,当我们上传到 GPU时,我们目前使用的是 float32。 所以我们用double计算,然后截断为single。 这可能比一次性完成更准确,但我想尝试与我们上传的数据类型保持一致。 不幸的是,这意味着单精度直到 GL 4.0,而 WebGL 2 只是 GL ES 3.0 :(

矩阵上传从来不是我们的瓶颈,我们在那些地方使用“uniform3fv”,它不是简单的操作,其次是一个drawCall,在大多数情况下,大缓冲区上传。 沉重的 pixi 应用程序每帧仅执行大约 400 次绘制调用,

在遇到用户的大坐标问题后,我更喜欢以双精度存储所有内容,直到我们上传为止。

此外,“a、b、c、d、tx、ty”符号比“0、1、3、4、6、7”更容易编写和阅读。 它也用于脊椎,除此之外,它们还有非常复杂的变换。 如果我们切换到矩阵,稍后检查我们的代码将不会那么容易。 对于某些人来说很难想象矩阵运算,但我很容易阅读它们。

更新。 我也认为这个对我们的帮助不仅仅是转换矩阵: https :

UPD2。 但是对于 3d 变换,Float32Array(16) 更好,我不会反对它。

我的票投给了表现最好的那个。 最后我使用数组上的对象检查它更快。 虽然我不能 100% 确定这是否仍然正确,但很可能已经改变了!

对于 3d,我喜欢 gl-matrix 风格,主要是因为很多东西会上传到 GPU! 对于 Pixi,通常情况并非如此。 大多数操作发生在 js 领域(例如 sprite 批处理器)。

https://jsperf.com/obj-vs-array-view-access/1

这是我做的测试。 对象比Float32Array慢,这两者是比正常阵列方式慢。 然后我们得到双精度最快的CPU速度它可以直接上传。

编辑:看起来Array结果是侥幸,我无法重现它?

我的票投给了表现最好的那个。
最后我使用数组上的对象检查它更快。

这是非常有趣的。 对象比纯数字数组更快似乎有悖常理,因为底层 vm 应该能够进行许多优化,知道对象语义可以在很大程度上被剥离。 我很好奇您是否有任何想法_为什么_对象可能会更快?

我的comp(i7,Windows 10)的相关结果:

铬 59:

image

火狐 54.0.1(32 位):

image

微软边缘 40.15063.0.0:

image

似乎我的Array更快的 chrome 结果是侥幸,不确定那是什么,但现在无法重现它。 它大约是 50M ops/s,但在我之前的一次运行中是 500M ops/s。 奇怪的...

无论哪种方式,Float32Array 成员在所有浏览器中始终与对象相同或更快。 这就是我切换它的原因,它的速度与以前相同(或更快),但我们现在避免转置和填充用于上传的数组。

你也可以用 Float64 做到这一点吗? :)

我更关心代码质量而不是每个 drawcall 多一个矩阵操作。

此外,我们不经常使用反转。 updateTransform() 是我们的问题。

我更关心代码质量而不是每个 drawcall 多一个矩阵操作。

Float64Array 如何提高代码质量? 我知道我们得到了更高的精度,但我不确定我是否理解为什么这如此重要,因为我们无论如何都要减少到单精度。

此外,我们不经常使用反转。 updateTransform() 是我们的问题。

这是对数据存储的读/写进行基准测试,对中间值执行的操作无关紧要。

我更关心代码质量而不是每个 drawcall 多一个矩阵操作。

我也同意这一点。 考虑到基于数组的矩阵、点和向量的“标准化”实际上可能更多的是代码质量改进而不是性能改进......我会考虑重用流行的 js 矩阵/向量库以及与流行渲染模块的更多互操作性成为代码质量的大赢家。

这是对数据存储的读/写进行基准测试,
对不相关的中间值执行的操作。

我认为你们都是对的。 这是数据存储读/写的一般基准测试。 但伊万有一个很好的观点; 如果updateTransform()是压倒性的操作,那么看到这一点将是令人信服的。

我担心一般的微观基准; 对于这些静态数据集,我想知道我们是否在这些测试中无意中利用了 javascript vms 中的编译器优化。 运行真实世界的测试会更有启发性(代价是需要做更多的设置工作。)

我也同意这一点。 考虑到基于数组的矩阵、点和向量的“标准化”实际上可能更多的是代码质量改进而不是性能改进......我会考虑重用流行的 js 矩阵/向量库以及与流行渲染模块的更多互操作性成为代码质量的大赢家。

我是“(a,b),(c,d),(tx,ty)”双精度矩阵,转换为float32array以上传并由“posX,posY,pivotX,pivotY,scaleX,scaleY,剪切X,剪切Y,rotZ”。 无论如何,我会在我的 fork 中使用它,但我也不想在 master pixi 中处理矩阵数组。 这是我的标准。

我也怀疑是否有可能让人们在 js 中使用一两个标准来进行 vec 数学。

Float64Array 如何提高代码质量? 我知道我们得到了更高的精度,但我不确定我是否理解为什么这如此重要,因为我们无论如何都要减少到单精度。

我们在 updateTransform 中将相机变换乘以精灵变换(用于滚动)。 结果只有在它很小的时候才适合屏幕,所以最终数字很小,但是精灵位置和相机位置都可以很大。 用户端解决方案:

1)计算他身边的一切。 PIXI 只处理相对较小的坐标。
2)将世界分成大坐标块,精灵有相对小的坐标,但这不适用于亚像素相机=>相机也需要大坐标和小坐标。

在大项目的情况下,强迫用户在他身边做是可以的,但对于小项目来说,这只是一个更令人头疼的问题。

我也怀疑是否有可能让人们在 js 中使用一两个标准来进行 vec 数学。

没明白你的意思,能详细点吗?

没明白你的意思,能详细点吗?

我不能,这对我来说太多了。

我们有在低级优化方面有不同经验的人。 和语言结构,DSL-s。 我们需要一个在某种程度上满足我们所有人的标准。

在过去的两年里,我用不同的变换制作了两个 pixi(用于 v3 和 v4)的叉子,我正在处理具有自己高级变换的“pixi-spine”,我正在制作第三个叉子。 从“过去的伊万”的角度来看,数组是最好的,因为它的形式最简单,并且有“gl-matrix”。

我同意@ivanpopelyshev 的观点,即保持 64 位很重要。 我试图弄清楚我们如何高效地做到这一点,而无需在每一帧创建和复制缓冲区。

也许我们将它存储为 Float64Array,上传时将其复制到 Float32Array。 这至少让我们使用类型化数组作为存储支持,它应该更容易复制到/从网络工作者复制。

Float64Array 那么,我只需要记住使用 (0,1), (3,4), (6,7) 作为 X,Y,translate

所有有趣的东西,我很希望我们在真实的 pixi 场景中对我们提出的解决方案进行基准测试——比如 bunnymark。

我在这方面的经验是,当我和@ivanpopelyshev上次切换到 gl-matrix for bunny mark 时,我们发现它的运行速度明显变慢(比如慢了三分之一!)

不过那是不久前的事了,我真的希望我在这里是错的!
如果速度差异现在可以忽略不计,那么我认为上面建议的路线将是王牌。

Pixi 速度很快:P 让我们确保在完全提交任何解决方案之前进行测试。

我们仍然需要 Float64Array 的基准。 这个实现对我来说是可以接受的,但我个人会在我的叉子中使用旧矩阵。 另外不要忘记添加属性只是为了兼容性。

还有一件事: @mreinstein ,我记得的标准之一是 PaperScript,它是一种专门针对 paper.js 的语言,将点操作直接添加到语言中。 如果 JS 有更多的语法糖,我们也会使用类似的东西。

有什么我可以帮忙的:这个更新的兔子标记? 我很高兴为此付出一些时间。

我非常有兴趣关注这里发生的性能问题。 不管结果如何。 如果基于数组的东西最终还是慢了很多,我真的很好奇背后的_why_。

需要注意的是:性能测试...最近(在 v59 中)chrome 的 v8 对 v8 发布了一些非常显着的变化,称为 turbofan。 据说它有一些显着的性能改进。

https://v8project.blogspot.com/2017/05/launching-ignition-and-turbofan.html

在 turbofan 出现之前的版本上运行更新的 bunnymark 可能会很有趣,而现在,只是为了它。

@GoodBoyDigital我更新了工作台以包含 Float64Array,并且读取/写入数组的速度始终比对象快。 如果它在 pixi 中变慢,那么其他东西就会改变,因为对 Float64Array 后备存储的读/写比对象快。

https://jsperf.com/obj-vs-array-view-access/1
image

只有在 Edge 上,物体速度匹配/超过 Float64Array,并且非常接近。

我已经在我的环境中测试过这个,我得到了类似的结果......到底是什么?! 为什么 Float64 数组读/写比等效的 Float32 数组操作快得多? 唯一想到的可能是 64 位浮点数在字边界上对齐? 我很困惑。

感谢@mreinstein的报价! 如果你能帮助我们进行一些性能测试,这些测试肯定会用冷酷的事实来平息整个辩论!

最好的办法是 fork pixi,然后用 gl-matrix 或@englercj矩阵类替换变换。 对于这个例子,我们真的只需要让精灵批处理也能工作——而不是整个引擎!

然后一旦我们有了一个调整过的版本,我们就可以在这里测试性能: https :
我们可以处理不同的数组类型。

@englercj太棒了! 看到这些结果确实令人鼓舞。 v5 版本是否接近可以让兔子标记旋转的状态?

@mreinstein只是一种预感,我认为这可能与从 64 位 -> 32 位的转换有关
因为js中的数字是64位对吗?

@GoodBoyDigital我想也许你是对的。 求助于我的朋友夫人。 谷歌,我偶然发现了这个:

https://stackoverflow.com/questions/15823021/when-to-use-float32array-instead-of-array-in-javascript

评分最高的答案(目前有 44 票)似乎合理且相关。

如果你能帮我们做一些性能测试

好的,很高兴为您提供帮助。 :)

最好的办法是 fork pixi

@GoodBoyDigital @englercj目前最好的分支是什么?

然后用 gl-matrix 或@englercj矩阵类替换变换。
对于这个例子,我们真的只需要让精灵批处理也能工作——而不是整个引擎!

你能再详细说明一下吗? 我不想将整个引擎转换为 gl-matrix 只是为了运行性能工作台......我在https://github.com/pixijs/bunny-mark 中没有看到任何精灵批处理的东西似乎有一个PIXI.Container 作为根元素,向其中添加兔子。 你是说我应该从 pixi.container、pixi.sprite 开始,然后从那里向后工作,直到依赖树找到所有需要替换转换的地方? 并不是说我不同意,只是想确保我有正确的策略来最小化不必要的工作。

updateTransform 内部有两个矩阵运算:

  1. 从位置、枢轴、缩放、旋转组成 - 无法改进。
  2. 乘以父矩阵 - 可以改进。

我建议使用由 Float64Array 支持的旧“a、b、c、d、tx、ty”道具制作矩阵类,并重写 updateTransform。 另外, @mreinstein做了足够多的事情来成为核心,我建议我们添加他,这样他就可以访问分支机构并建立农场。

所以他可以访问分支机构并建立农场。

每个人都可以访问它,fork + PR 可以完成所有这些。

我很好奇你们在兔子标记中得到什么样的结果......当我尝试使用默认的 100k bunnies 的 chrome 的各种分支(开发、发布、其他)时,我始终获得 10-16fps。 对运行超过 8.75 秒的代码进行性能分析:

screen shot 2017-07-02 at 11 58 03 am

几乎所有的时间都花在了 javascript 调用上。

screen shot 2017-07-02 at 11 57 50 am

在 JavaScript 上花费的时间中,大部分时间实际上花在了Sprite.calculateVertices() 。 比TransformStatic.updateTransform()花费的多 4 倍

在 Firefox 中,我的帧率大约是两倍,但花费的时间细分是相似的; calculateVertices()在两种浏览器中都占用了大量时间。 这是预期的吗? 您在 bunnymark 运行中是否得到了类似的结果?

这是兔子的预期。 我们正在谈论的矩阵运算在两个地方执行:用于乘法的 upateTransform() 和每次调用一次的 flush()。 在大规模上没关系。

我糊涂了。 :(我怎么知道该基准测试的性能?我的印象是它主要基于实现的纯 fps。如果这是真的,那么我的帧速率正在被顶点计算_severely_ 节流。看我粘贴的第一个图表上面,渲染是总帧时间的一小部分。顶点和变换更新压倒了总帧时间。

如果这是错误的,并且 fps 不是衡量我的 pixi 构建性能如何的指标,那么我使用什么标准来衡量给定构建?

它可以在 GPU 端的 CPU 上进行节流(未显示),

如果您的 FPS 远低于 60,但“空闲”很大 - 它的 GPU。

如果空闲很小,那么它的 CPU。

calculateVertices 内部有矩阵运算 - 只需将四个角乘以矩阵即可。

TS fork 的一个小想法:添加内联矩阵类型属性的变换。 遗憾的是,TS 还没有那种转换:(

如果我们找到一种方法来注释矩阵变量,我认为它也可以用于 babel。

我无法理解发生了什么。

Alt text

需要记住的一点:这个基准测试的目标首先是看看使用对象与 gl-matrix 是否会像人们认为的那样对性能造成巨大的影响。 不管我们的基准测试结果有何不同,我们似乎始终如一地表明对象性能并不比大多数阵列情况差。

我也想避免在这里得出关于 pixi 的一般性能结论,因为我已经描述了兔子标记。 在 Chrome 中,> 50% 的程序总时间花费在Sprite.calculateVertices() (40% ish),其次是TransformStatic.updateTransform() (11% ish) Firefox 似乎运行速度快两倍,但比例花在这两个功能上的时间仍然一致。

我想避免离题太远,但我会说在做这个 bunnymark 分析时,我开始认为我们对 es5 getter/setter 的使用可能与 chrome 的性能下降有关: https: //jsperf.com/es5-getters-setters-versus-getter-setter-methods/10

你们有人在这里闲逛吗? 我认为半实时聊天比在这里传递消息更容易。

你给@mreinstein发什么电子邮件? 会请你懈怠👍

根据@englercj所说的,显然我们没有这样做。 关闭。

只是小更新:我在https://github.com/pixijs/pixi-projection/blob/master/src/Matrix2d.ts 中有 3x3 矩阵而不是 3x2,基于 Float64Array。

由于关闭后没有任何近期活动,因此该线程已自动锁定。 请为相关错误打开一个新问题。

此页面是否有帮助?
0 / 5 - 0 等级