背景:
我想根据Android相机应用的代码添加实时过滤器。但Android相机应用程序的架构基于OpenGL ES 1.x.我需要使用着色器来自定义我们的过滤器实现。但是,将相机应用程序更新为OpenGL ES 2.0非常困难。然后我必须找到一些其他方法来实现实时过滤器而不是OpenGL。经过一些研究,我决定使用渲染脚本。
问题:
我已经通过渲染脚本编写了一个简单过滤器的演示。它表明fps远低于OpenGL实现它。大约5 fps vs 15 fps。
问题:
Android官方异地说:RenderScript运行时将并行处理设备上可用的所有处理器(如多核CPU,GPU或DSP)的工作,使您可以专注于表达算法而不是安排工作或负载均衡。那么为什么渲染脚本实现较慢?
如果渲染脚本无法满足我的要求,还有更好的方法吗?
代码详情:
嗨,我和提问者在同一个团队中。我们想要编写一个基于渲染脚本的实时滤镜相机。在我们的测试演示项目中,我们使用了一个简单的过滤器:添加了覆盖过滤器ScriptC脚本的YuvToRGB IntrinsicScript。 在OpenGL版本中,我们将相机数据设置为纹理,并使用着色器执行image-filter-procss。像这样:
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureYHandle);
GLES20.glUniform1i(shader.uniforms.get("uTextureY"), 0);
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mTextureWidth,
mTextureHeight, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE,
mPixelsYBuffer.position(0));
在RenderScript版本中,我们将相机数据设置为Allocation,并使用script-kernals执行image-filter-procss。像这样:
// The belowing code is from onPreviewFrame(byte[] data, Camera camera) which gives the camera frame data
byte[] imageData = datas[0];
long timeBegin = System.currentTimeMillis();
mYUVInAllocation.copyFrom(imageData);
mYuv.setInput(mYUVInAllocation);
mYuv.forEach(mRGBAAllocationA);
// To make sure the process of YUVtoRGBA has finished!
mRGBAAllocationA.copyTo(mOutBitmap);
Log.e(TAG, "RS time: YUV to RGBA : " + String.valueOf((System.currentTimeMillis() - timeBegin)));
mLayerScript.forEach_overlay(mRGBAAllocationA, mRGBAAllocationB);
mRGBAAllocationB.copyTo(mOutBitmap);
Log.e(TAG, "RS time: overlay : " + String.valueOf((System.currentTimeMillis() - timeBegin)));
mCameraSurPreview.refresh(mOutBitmap, mCameraDisplayOrientation, timeBegin);
这两个问题是: (1)RenderScript进程似乎比OpenGL进程慢。 (2)根据我们的时间日志,使用内在脚本的YUV到RGBA的过程非常快,大约需要6ms;但是使用scriptC的叠加过程非常慢,大约需要180ms。这是怎么发生的?
这是我们使用的ScriptC的rs-kernal代码(mLayerScript):
#pragma version(1)
#pragma rs java_package_name(**.renderscript)
#pragma stateFragment(parent)
#include "rs_graphics.rsh"
static rs_allocation layer;
static uint32_t dimX;
static uint32_t dimY;
void setLayer(rs_allocation layer1) {
layer = layer1;
}
void setBitmapDim(uint32_t dimX1, uint32_t dimY1) {
dimX = dimX1;
dimY = dimY1;
}
static float BlendOverlayf(float base, float blend) {
return (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)));
}
static float3 BlendOverlay(float3 base, float3 blend) {
float3 blendOverLayPixel = {BlendOverlayf(base.r, blend.r), BlendOverlayf(base.g, blend.g), BlendOverlayf(base.b, blend.b)};
return blendOverLayPixel;
}
uchar4 __attribute__((kernel)) overlay(uchar4 in, uint32_t x, uint32_t y) {
float4 inPixel = rsUnpackColor8888(in);
uint32_t layerDimX = rsAllocationGetDimX(layer);
uint32_t layerDimY = rsAllocationGetDimY(layer);
uint32_t layerX = x * layerDimX / dimX;
uint32_t layerY = y * layerDimY / dimY;
uchar4* p = (uchar4*)rsGetElementAt(layer, layerX, layerY);
float4 layerPixel = rsUnpackColor8888(*p);
float3 color = BlendOverlay(inPixel.rgb, layerPixel.rgb);
float4 outf = {color.r, color.g, color.b, inPixel.a};
uchar4 outc = rsPackColorTo8888(outf.r, outf.g, outf.b, outf.a);
return outc;
}
答案 0 :(得分:3)
Renderscript不使用任何GPU或DSP核心。这是Google故意模糊的文档所鼓励的常见误解。 Renderscript曾经有一个OpenGL ES的界面,但已被弃用,除了动画壁纸之外从未使用过。 Renderscript将使用多个CPU内核(如果可用),但我怀疑Renderscript将被OpenCL取代。
查看Android SDK中的Effects类和Effects演示。它展示了如何使用OpenGL ES 2.0着色器将效果应用于图像,而无需编写OpenGL ES代码。
<强>更新强>
当我学到更多回答问题而不是问一个问题时,这真是太棒了。这就是这种情况。你可以从缺乏答案的角度看出Renderscript很难在Google之外使用,因为它的奇怪架构忽视了OpenCL等行业标准,而且几乎不存在关于它如何实际工作的文档。 尽管如此,我的回答确实引起了Renderscrpt开发团队的罕见回应,其中只包含一个实际包含有关renderscript的有用信息的链接 - 这篇文章来自IMG的Alexandru Voica,PowerVR GPU供应商:
那篇文章有一些对我来说很陌生的好信息。有更多的人在那里发布评论,他们无法让Renderscript代码真正在GPU上运行。
但是,我不认为Renderscript不再在Google开发。虽然我声明“Renderscript不使用任何GPU或DSP核心”。直到最近才发生这种情况,我已经了解到Jelly Bean发布之后已经发生了变化。 如果其中一个Renderscript开发人员可以解释这一点,那将会很棒。或者即使他们有一个解释该列表的公共网页 实际上支持哪些GPU,以及如何判断代码是否实际在GPU上运行。
我的观点是谷歌最终将用OpenCL取代Renderscript,我不会花时间用它来开发。