绘制路径和硬件加速

Drawing paths and hardware acceleration

提问人:Mad Scientist 提问时间:2/23/2013 最后编辑:Mad Scientist 更新时间:12/22/2014 访问量:14283

问:

在我看来,我正在绘制一条相当大的路径,并且遇到了一些性能问题。该路径目前长 32,000 个点,但我的应用程序应扩展到至少 128,000 个点。我真的无法对路径的大小做任何事情,因为数据集太大了,我需要能够一次显示整个路径并允许放大。

我使用的是运行 Android 4.2 的 Nexus 10,默认情况下,它为未明确禁用它的应用程序启用硬件加速。

该路径是使用以下代码创建的(我省略了一些设置和其他不相关的部分):

dataPath.moveTo(0, offset - (float) data[leftLimit]/ scalingFactor);
        for (int i = leftLimit; i < rightLimit; ++i) {
            x = (i - leftLimit) * dx;
            y = offset - (float) data[i]/ scalingFactor;
            dataPath.lineTo(x, y);
        }

然后用方法画出:onDraw()

canvas.drawColor(Color.WHITE);
canvas.drawPath(dataPath, linePaint);

我测量了使用和不使用硬件加速绘制视图所需的时间,令我惊讶的是,硬件加速要慢得多:adb shell dumpsys gfxinfo

使用硬件加速:

enter image description here

没有硬件加速:

enter image description here

硬件加速版本每帧大约需要 200-300 毫秒,大部分时间在处理阶段。非加速版本大约需要 50 毫秒,其中 2/3 在绘制阶段,1/3 在处理阶段。

显然,即使是没有硬件加速的更快版本,仍然太慢,无法达到 60 fps,或者当我移动到更大的数据集时甚至几乎无法使用。

在我的情况下,呈现位图的路径,然后仅转换该位图以适合屏幕的想法也是有问题的。我需要支持在路径上放大得很远,并且为了在不使路径质量变差的情况下启用放大,我将不得不渲染路径的超大位图(并且可能会遇到内存限制和纹理大小限制)。当放大到很远时,我将不得不只为路径的一部分创建更新的图像,或者切换到直接渲染路径,如果性能仍然与我现在相似,这可能会导致延迟大于帧速率。

我现在想知道的是

  • 绘制线条/路径只是 GPU 不擅长的事情,不应该尝试硬件加速,还是我可能做错了什么导致性能不佳?
  • 我能做些什么来绘制具有可接受性能的如此巨大的路径吗?
Android 图形 OpenGL-ES

评论

0赞 Nicol Bolas 2/23/2013
OpenGL 究竟是如何适应的?
0赞 Mad Scientist 2/23/2013
据我了解,硬件加速使用 OpenGL。但我不确定在那里使用哪个标签。
1赞 Automatico 2/23/2013
你是怎么画的?如果你一次向 GPU 绘制 1 个点,那么你可能会得到更差的性能,但如果你把它全部批处理,你应该会看到增加,至少在理论上是这样。
1赞 Mad Scientist 2/23/2013
@Cort3z我添加了绘制路径的代码,我正在创建完整的路径,然后一次性绘制它。
1赞 Pedro Loureiro 3/6/2013
如果你被缩小了,画128k点真的有什么意义吗?最有可能的是,这些将是无法区分的。我建议,如果你是缩小的,你可以跳过或平均其中的一些点。

答:

5赞 Automatico 2/23/2013 #1

似乎您可能会在 GPU 中遇到一般瓶颈。请查看以下链接:

如何使路线绘制更高效

Android 画布路径实时性能

从 canvas 切换到 OpenGL

尤其是第一个,它建议你制作一个位图并绘制它。看起来如果你改用 openGL,你可以获得更好的性能。可能有一些神奇的方法可以使现有方法起作用,但我目前还不知道这一点。

为什么你会看到你的行为,可能是因为你在每次绘制之间将所有数据发送到GPU。您应该将其缓存在 GPU 上,并使用翻译代替。不重新创建数据并将其全部发送到 GPU。您可能受 I/O 限制,这意味着 CPU 和 GPU 之间的传输速率是限制性能的原因。根据您提供的数据,我无法 100% 确定这一点,但这是我最好的猜测。尝试不同的缓存技术,主要是链接 #1 中的 png-cache。

51赞 Romain Guy 3/5/2013 #2

绘制线条/路径只是 GPU 不擅长的东西吗 不应该尝试硬件加速,或者我可能会做点什么 导致性能不佳的错误?

路径始终使用 CPU 呈现。当应用进行硬件加速时,这意味着渲染器将首先使用 CPU 将路径绘制到位图中,然后将该位图作为纹理上传到 GPU,最后在屏幕上绘制纹理。

线路完全由硬件加速。在这种情况下,我建议您不要使用 a。PathCanvas.drawLines()

我能做些什么来画出如此巨大的路径 性能?

您应该自己使用路径或将路径呈现为您管理的路径。Canvas.drawLines()Bitmap

评论

2赞 barthand 5/7/2013
至于手动绘制路径并允许它们在屏幕上移动,我在这里推送了简单的代码。我使用相同的技术在 GPU 加速的 MapView 上将此类路径绘制为叠加层(由于 GPU 上的纹理大小限制,在叠加层上绘制路径的通常 GPU 加速方法会导致错误)。BitmapShape path too large to be rendered into a texture
0赞 Robert 11/1/2017
对我来说,使用一批点,而不是多次调用单个点,显着提高了性能。Canvas.drawLines(float[], Paint)Canvas.drawLine(int. int, int, int)
0赞 Robert 11/1/2017
是否有官方网站或文档指出 Path 使用 CPU 并使用硬件加速?我之所以问这个问题,是因为我的表现非常糟糕,搜索了性能技巧,但没有找到有意义的东西,直到我读到这个答案。Canvas.drawLines()
0赞 Romain Guy 11/8/2017
在最底部有一个提及:developer.android.com/guide/topics/graphics/hardware-accel.html
0赞 makovkastar 3/19/2019
@RomainGuy我开始使用 drawLines 而不是 drawPath,并看到了显着的性能改进。但是,我找不到像使用 drawPath 那样很好地连接线的方法,请查看此图像:i.stack.imgur.com/NMpFo.png。你知道如何解决这个问题吗?
3赞 Arne Bergene Fossaa 3/7/2013 #3

绘制路径将涉及的不仅仅是“告诉”GPU 绘制线条。路径具有许多特征,例如如何处理线的末端或线的虚线,这些特征不是 GPU 加速的。当您遇到那么多行时,可能会有另一个小插曲,使 CPU 和 GPU 加速的组合变慢。上面建议的解决方案是移动到而不是尝试不使用花哨的方法可能会有所帮助。canvas.drawLinescanvs.drawPathPaint

如果这没有帮助,您可以尝试使用 GLSurfaceView 并将线条直接渲染到其中。这将涉及一些 OpenGL ES 知识,但应该不会非常困难,而且可能会更有效。

评论

0赞 Dave Hubbard 4/6/2018
虽然与硬件加速问题没有直接关系(但与给出的示例相关),但我发现使用 Matrix 类来缩放和转换点和路径列表比在代码中循环快很多倍。快 20 到 50 倍。我有一些需要路径的复杂区域。缩放从循环的 240 毫秒增加到矩阵的 6 毫秒。matrix.setScale(factor, factor, centerX, centerY),然后是 path.transform(matrix)。或者 matrix.mapPoints(pointsarray)。