【发布时间】:2014-02-14 08:25:43
【问题描述】:
背景:
我想根据 Android 相机应用的代码添加实时滤镜。但 Android 相机应用的架构是基于 OpenGL ES 1.x。我需要使用着色器来自定义我们的过滤器实现。但是,将相机应用程序更新到 OpenGL ES 2.0 太难了。然后我必须找到一些其他方法来实现实时过滤器而不是OpenGL。经过一番研究,我决定使用渲染脚本。
问题:
我已经编写了一个简单的渲染脚本过滤器的演示。它表明 fps 比通过 OpenGL 实现它要低得多。大约 5 fps 与 15 fps。
问题:
Android 官方站外表示:RenderScript 运行时将并行处理设备上所有可用处理器的工作,例如多核 CPU、GPU 或 DSP,让您可以专注于表达算法而不是调度工作或负载均衡。那为什么渲染脚本执行慢呢?
如果渲染脚本不能满足我的要求,有没有更好的方法?
代码详情:
您好,我和提问者在同一个团队。我们想编写一个基于渲染脚本的实时滤镜相机。在我们的测试演示项目中,我们使用了一个简单的过滤器:添加了一个覆盖过滤器 ScriptC 脚本的 YuvToRGB IntrinsicScript。 在 OpenGL 版本中,我们将相机数据设置为纹理,并使用着色器进行图像过滤处理。像这样:
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 的覆盖过程非常慢,大约需要 180 毫秒。这是怎么发生的?
这是我们使用的ScriptC(mLayerScript)的rs-kernal代码:
#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;
}
【问题讨论】:
-
你能分享一下两个版本的代码有什么不同吗?我怀疑问题是将相机中的数据输入 RS。
-
@R.JasonSams 感谢您的回复。我已经编辑了我的问题。并添加了一些代码。
-
1.不要使用 rsAllocationGetDimX。将它们作为全局变量传递(如 dimX 和 dimY)。 2. 不要忘记常量上的 f 后缀。您现在正在使用双精度。 3. 使用 rsGetElementAt_uchar4,而不是 rsGetElementAt。 4.不要包含rs_graphics.rsh,没必要。 5.考虑缓存layerDimX/DimX为全局(与Y相同)。 6. 尝试#pragma rs_fp_relaxed,如果您不关心严格的 IEEE-754 合规性(NEON 和某些 GPU 需要放松),可以启用一些额外的优化。这些是亮点。
-
Tim 获得了大部分的高分,如果您不需要 rsPackColorTo8888() 所做的范围重新缩放(0-255 与 0-1),您也可以使用 convert_uchar4() 和 convert_float4()。
-
谢谢蒂姆和杰森。我们将尝试根据您的观点修改我们的代码。你有关于渲染脚本代码优化的文章吗?我们用谷歌搜索这样的文章有点困难。
标签: android opengl-es renderscript