Renderscript falla en el controlador habilitado para GPU si USAGE_SHARED

Estamos usando renderscript para el procesamiento de audio dsp. Es simple y mejora significativamente el rendimiento para nuestro caso de uso. Pero nos encontramos con un problema molesto conUSAGE_SHARED en dispositivos que tienen un controlador personalizado con ejecución de GPU habilitada.

Como podrias saber,USAGE_SHARED flag realiza la asignación de renderscript para reutilizar la memoria dada sin tener que crear una copia de la misma. Como consecuencia, no solo ahorra memoria, en nuestro caso, mejora el rendimiento al nivel deseado.

El siguiente código conUSAGE_SHARED funciona bien en el controlador de renderizado predeterminado libRSDriver.so). Con controlador personalizado libRSDriver_adreno.so) USAGE_SHARED no reutiliza la memoria dada y, por lo tanto, los datos.

Este es el código que hace uso deUSAGE_SHARED y llama al kernel de renderscript

void process(float* in1, float* in2, float* out, size_t size) {
  sp<RS> rs = new RS();
  rs->init(app_cache_dir);

  sp<const Element> e = Element::F32(rs);
  sp<const Type> t = Type::create(rs, e, size, 0, 0);

  sp<Allocation> in1Alloc = Allocation::createTyped(
                rs, t,
                RS_ALLOCATION_MIPMAP_NONE, 
                RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
                in1);

  sp<Allocation> in2Alloc = Allocation::createTyped(
                rs, t,
                RS_ALLOCATION_MIPMAP_NONE, 
                RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
                in2);

  sp<Allocation> outAlloc = Allocation::createTyped(
                rs, t,
                RS_ALLOCATION_MIPMAP_NONE, 
                RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
                out);

  ScriptC_x* rsX = new ScriptC_x(rs);
  rsX->set_in1Alloc(in1Alloc);
  rsX->set_in2Alloc(in2Alloc);
  rsX->set_size(size);

  rsX->forEach_compute(in1Alloc, outAlloc);
}

NOTA: Esta variación deAllocation::createTyped() no se menciona en la documentación, pero el códigorsCppStructs.h lo tiene. Este es el método de asignación de fábrica que permite proporcionar puntero de respaldo y respetaUSAGE_SHARED bandera. Así es como se declara:

/**
 * Creates an Allocation for use by scripts with a given Type and a backing pointer. For use
 * with RS_ALLOCATION_USAGE_SHARED.
 * @param[in] rs Context to which the Allocation will belong
 * @param[in] type Type of the Allocation
 * @param[in] mipmaps desired mipmap behavior for the Allocation
 * @param[in] usage usage for the Allocation
 * @param[in] pointer existing backing store to use for this Allocation if possible
 * @return new Allocation
 */
static sp<Allocation> createTyped(
            const sp<RS>& rs, const sp<const Type>& type,
            RsAllocationMipmapControl mipmaps, 
            uint32_t usage, 
            void * pointer);

Este es el kernel de renderscript

rs_allocation in1Alloc, in2Alloc;
uint32_t size;

// JUST AN EXAMPLE KERNEL
// Not using reduction kernel since it is only available in later API levels.
// Not sure if support library helps here. Anyways, unrelated to the current problem

float compute(float ignored, uint32_t x) {
  float result = 0.0f;
  for (uint32_t i=0; i<size; i++) {
    result += rsGetElementAt_float(in1Alloc, x) * rsGetElementAt_float(in2Alloc, size-i-1); // just an example computation
  }

  return result;
}

Como se mencionó,out no tiene ninguno de los resultados del cálculo. @syncAll(RS_ALLOCATION_USAGE_SHARED) tampoco ayudó.

Sin embargo, lo siguiente funciona (pero mucho más lento)

void process(float* in1, float* in2, float* out, size_t size) {
  sp<RS> rs = new RS();
  rs->init(app_cache_dir);

  sp<const Element> e = Element::F32(rs);
  sp<const Type> t = Type::create(rs, e, size, 0, 0);

  sp<Allocation> in1Alloc = Allocation::createTyped(rs, t);
  in1Alloc->copy1DFrom(in1);

  sp<Allocation> in2Alloc = Allocation::createTyped(rs, t);
  in2Alloc->copy1DFrom(in2);

  sp<Allocation> outAlloc = Allocation::createTyped(rs, t);

  ScriptC_x* rsX = new ScriptC_x(rs);
  rsX->set_in1Alloc(in1Alloc);
  rsX->set_in2Alloc(in2Alloc);
  rsX->set_size(size);

  rsX->forEach_compute(in1Alloc, outAlloc);
  outAlloc->copy1DTo(out);
}

La copia hace que funcione, pero en nuestras pruebas, copiar de un lado a otro degrada significativamente el rendimiento.

Si apagamos la ejecución de GPU a través dedebug.rs.default-CPU-driver propiedad del sistema, podríamos ver que el controlador personalizado funciona bien con el rendimiento deseado.

Alinear la memoria dada al renderscript a 16,32, .., o 1024, etc. no ayudó a que el controlador personalizado respetase a USAGE_SHARED.

Pregunt

Entonces, nuestra pregunta es: ¿Cómo hacer que este kernel funcione para dispositivos que usan un controlador de renderizado personalizado que permite la ejecución de GPU?

Respuestas a la pregunta(1)

Su respuesta a la pregunta