Renderizar script es mucho más lento que OpenGL en Android
opengl-es renderscript (1)
Renderscript no utiliza núcleos de GPU o DSP. Esa es una idea equivocada común alentada por la documentación deliberadamente vaga de Google. Renderscript solía tener una interfaz para OpenGL ES, pero que ha quedado en desuso y nunca se ha utilizado para mucho más que fondos de pantalla animados. Renderscript usará múltiples núcleos de CPU, si está disponible, pero sospecho que Renderscript será reemplazado por OpenCL.
Eche un vistazo a la clase de efectos y la demostración de efectos en el SDK de Android. Muestra cómo usar los sombreadores de OpenGL ES 2.0 para aplicar efectos a las imágenes sin escribir el código de OpenGL ES.
ACTUALIZAR:
Es maravilloso cuando aprendo más respondiendo una pregunta que preguntando uno y ese es el caso aquí. Se puede ver por la falta de respuestas que Renderscript apenas se usa fuera de Google debido a su extraña arquitectura que ignora los estándares de la industria como OpenCL y la documentación casi inexistente sobre cómo funciona realmente. No obstante, mi respuesta evocó una rara respuesta del equipo de desarrollo de Renderscrpt que incluye solo un enlace que contiene información útil sobre renderscript, este artículo de Alexandru Voica en IMG, el proveedor de la GPU PowerVR:
Ese artículo tiene buena información que era nueva para mí. Hay comentarios publicados allí de más personas que tienen problemas para obtener el código de Renderscript para que realmente se ejecute en la GPU.
Pero era incorrecto suponer que Renderscript ya no se está desarrollando en Google. Aunque mi afirmación de que "Renderscript no utiliza ningún núcleo de GPU o DSP". fue cierto hasta hace muy poco tiempo, he aprendido que esto ha cambiado a partir de uno de los lanzamientos de Jelly Bean. Hubiera sido genial si uno de los desarrolladores de Renderscript podría haber explicado eso. O incluso si tuvieran una página web pública que lo explica o enumera qué GPU son realmente compatibles y cómo puede saber si su código realmente se ejecuta en una GPU.
Mi opinión es que Google reemplazará Renderscript con OpenCL eventualmente y no invertiría tiempo desarrollando con él.
FONDO:
Quiero agregar un filtro en vivo basado en el código de la aplicación de la cámara de Android. Pero la arquitectura de la aplicación de la cámara Android se basa en OpenGL ES 1.x. Necesito usar shader para personalizar la implementación de nuestro filtro. Sin embargo, es demasiado difícil actualizar la aplicación de la cámara a OpenGL ES 2.0. Entonces tengo que encontrar algunos otros métodos para implementar filtro en vivo en lugar de OpenGL. Decidí usar el script de renderización después de algunas investigaciones.
PROBLEMA:
He escrito una demostración de un filtro simple mediante el script de renderizado. Muestra que el fps es mucho más bajo que OpenGL. Alrededor de 5 fps frente a 15 fps.
PREGUNTAS:
El sitio externo de Android dice: El tiempo de ejecución de RenderScript paralelizará el trabajo en todos los procesadores disponibles en un dispositivo, como CPU multinúcleo, GPU o DSP, permitiéndole concentrarse en la expresión de algoritmos en lugar de programar el trabajo o el equilibrio de carga. Entonces, ¿por qué es más lenta la implementación del script de renderizado?
Si el script de renderización no puede satisfacer mis requisitos, ¿hay alguna forma mejor?
DETALLES DEL CÓDIGO:
Hola, estoy en el mismo equipo que el que pregunta. Queremos escribir una cámara de filtro en vivo basada en script de renderizado. En nuestro proyecto de prueba-demostración, utilizamos un filtro simple: un YuvToRGB IntrinsicScript agregado con un script ScriptC de filtro de superposición. En la versión de OpenGL, configuramos los datos de la cámara como texturas y realizamos la imagen-filtro-proceso con sombreador. Me gusta esto:
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));
En la versión RenderScript, configuramos los datos de la cámara como Asignación y realizamos la imagen-filtro-proceso con script-kernals. Me gusta esto:
// 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);
Los dos problemas son: (1) El proceso RenderScript parece más lento que el proceso OpenGL. (2) De acuerdo con nuestro registro de tiempo, el proceso de YUV a RGBA que usa script intrínseco es muy rápido, toma aproximadamente 6 ms; pero el proceso de superposición que usa scriptC es muy lento, toma aproximadamente 180 ms. ¿Como sucedió esto?
Aquí está el código rs-kernal del ScriptC que usamos (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;
}