Debido a que las aplicaciones que utilizan RenderScript aún se ejecutan dentro de la VM de Android, tienes acceso a todas las API de framework que conoces, pero puedes usar RenderScript cuando corresponda. Para posibilitar esta interacción entre el framework y el entorno de ejecución de RenderScript, también hay una capa intermedia de código que facilita la comunicación y la administración de la memoria entre los dos niveles de código. En este documento, se proporcionan más detalles sobre las diferentes capas de código y sobre la manera en que se comparte la memoria entre la VM de Android y el entorno de ejecución de RenderScript.
Capa del entorno de ejecución de RenderScript
Tu código de RenderScript se compila y se ejecuta en una capa de entorno de ejecución compacta y bien definida. Las API de entorno de ejecución de RenderScript ofrecen compatibilidad para procesamiento intensivo que es portátil y escalable automáticamente a la cantidad de núcleos disponibles en un procesador.
Nota: Se debe garantizar que las funciones C estándar en el NDK se ejecuten en una CPU, por lo que RenderScript no puede acceder a esas bibliotecas, ya que está diseñado para ejecutarse en diferentes tipos de procesadores.
Define tu código de RenderScript en archivos .rs
y .rsh
en el directorio src/
de tu proyecto de Android. El compilador llvm
que se ejecuta como parte de una compilación de Android compila el código en un código de bytes intermedio. Cuando la aplicación se ejecuta en un dispositivo, se compila el bytecode (justo a tiempo) en código máquina con otro compilador llvm
que se encuentre en el dispositivo. El código máquina está optimizado para el dispositivo y también almacenado en caché, por lo que los usos posteriores de la aplicación habilitada para RenderScript no vuelven a compilar el código de bytes.
Algunas funciones clave de las bibliotecas de entorno de ejecución de RenderScript son las siguientes:
- Funciones de solicitud de asignación de memoria
- Un gran grupo de funciones matemáticas con versiones sobrecargadas de tipo escalar y vectorial de muchas rutinas comunes (están disponibles las operaciones como la suma, la multiplicación, el producto de puntos y el producto cruzado, así como las funciones de comparación y de aritmética atómica)
- Rutinas de conversión para vectores y tipos de datos primitivos, rutinas matriciales y rutinas de fecha y hora
- Estructuras y tipos de datos para admitir el sistema RenderScript, como los tipos de vectores para definir dos, tres o cuatro vectores
- Funciones de acceso
Consulta la referencia de la API de entorno de ejecución de RenderScript para obtener más información sobre las funciones disponibles.
Capa reflejada
La capa reflejada es un conjunto de clases que generan las herramientas de compilación de Android para otorgar acceso al entorno de ejecución de RenderScript desde el framework de Android. Esta capa también proporciona métodos y constructores que permiten asignar y trabajar con memoria para punteros que se definen en el código de RenderScript. En la siguiente lista, se describen los componentes principales que se reflejan:
- Cada archivo
.rs
que creas se genera en una clase llamadaproject_root/gen/package/name/ScriptC_renderscript_filename
de tipoScriptC
. Este archivo es la versión.java
de tu archivo.rs
, al que puedes llamar desde el framework de Android. En esta clase, se incluyen los siguientes elementos reflejados del archivo.rs
:- Funciones no estáticas
- Variables RenderScript globales no estáticas. Los métodos de acceso se generan para cada variable, por lo que puedes leer y escribir las variables de RenderScript desde el framework de Android. Si se inicializa una variable global en la capa de entorno de ejecución de RenderScript, esos valores se utilizan para inicializar los valores correspondientes en la capa de framework de Android. Si las variables globales están marcadas como
const
, no se genera un métodoset
. Busca más detalles aquí. - Punteros globales
- Una
struct
se refleja en su propia clase denominadaproject_root/gen/package/name/ScriptField_struct_name
, que extiendeScript.FieldBase
. Esta clase representa un arreglo destruct
, que te permite asignar memoria para una o más instancias de estastruct
Funciones
Las funciones se reflejan en la propia clase de secuencia de comandos, ubicada en project_root/gen/package/name/ScriptC_renderscript_filename
. Por ejemplo, si defines la siguiente función en el código de RenderScript:
void touch(float x, float y, float pressure, int id) { if (id >= 10) { return; } touchPos[id].x = x; touchPos[id].y = y; touchPressure[id] = pressure; }
se genera el siguiente código Java:
public void invoke_touch(float x, float y, float pressure, int id) { FieldPacker touch_fp = new FieldPacker(16); touch_fp.addF32(x); touch_fp.addF32(y); touch_fp.addF32(pressure); touch_fp.addI32(id); invoke(mExportFuncIdx_touch, touch_fp); }
Las funciones no pueden tener valores de resultado, porque el sistema RenderScript está diseñado para ser asíncrono. Cuando tu código del framework de Android llama a RenderScript, la llamada se pone en cola y se ejecuta cuando es posible. Esta restricción permite que el sistema RenderScript funcione sin interrupciones constantes y aumenta la eficiencia. Si se permitiera que las funciones tuvieran valores de resultado, la llamada se bloquearía hasta que se mostrara el valor.
Si deseas que el código de RenderScript envíe un valor al framework de Android, usa la función rsSendToClient()
.
Variables
Las variables de los tipos admitidos se reflejan en la propia clase de secuencia de comandos, ubicada en project_root/gen/package/name/ScriptC_renderscript_filename
. Para cada variable, se genera un conjunto de métodos de acceso. Por ejemplo, si defines la siguiente variable en el código de RenderScript:
uint32_t unsignedInteger = 1;
se genera el siguiente código Java:
private long mExportVar_unsignedInteger; public void set_unsignedInteger(long v){ mExportVar_unsignedInteger = v; setVar(mExportVarIdx_unsignedInteger, v); } public long get_unsignedInteger(){ return mExportVar_unsignedInteger; }
Structs
Las structs se reflejan en sus propias clases, ubicadas en <project_root>/gen/com/example/renderscript/ScriptField_struct_name
. Esta clase representa un arreglo de las struct
y te permite asignar memoria para un número especificado de struct
. Por ejemplo, si defines la siguiente struct:
typedef struct Point { float2 position; float size; } Point_t;
se genera el siguiente código en ScriptField_Point.java
:
package com.example.android.rs.hellocompute; import android.renderscript.*; import android.content.res.Resources; /** * @hide */ public class ScriptField_Point extends android.renderscript.Script.FieldBase { static public class Item { public static final int sizeof = 12; Float2 position; float size; Item() { position = new Float2(); } } private Item mItemArray[]; private FieldPacker mIOBuffer; public static Element createElement(RenderScript rs) { Element.Builder eb = new Element.Builder(rs); eb.add(Element.F32_2(rs), "position"); eb.add(Element.F32(rs), "size"); return eb.create(); } public ScriptField_Point(RenderScript rs, int count) { mItemArray = null; mIOBuffer = null; mElement = createElement(rs); init(rs, count); } public ScriptField_Point(RenderScript rs, int count, int usages) { mItemArray = null; mIOBuffer = null; mElement = createElement(rs); init(rs, count, usages); } private void copyToArray(Item i, int index) { if (mIOBuffer == null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */); mIOBuffer.reset(index * Item.sizeof); mIOBuffer.addF32(i.position); mIOBuffer.addF32(i.size); } public void set(Item i, int index, boolean copyNow) { if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */]; mItemArray[index] = i; if (copyNow) { copyToArray(i, index); mAllocation.setFromFieldPacker(index, mIOBuffer); } } public Item get(int index) { if (mItemArray == null) return null; return mItemArray[index]; } public void set_position(int index, Float2 v, boolean copyNow) { if (mIOBuffer == null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */); if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */]; if (mItemArray[index] == null) mItemArray[index] = new Item(); mItemArray[index].position = v; if (copyNow) { mIOBuffer.reset(index * Item.sizeof); mIOBuffer.addF32(v); FieldPacker fp = new FieldPacker(8); fp.addF32(v); mAllocation.setFromFieldPacker(index, 0, fp); } } public void set_size(int index, float v, boolean copyNow) { if (mIOBuffer == null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */); if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */]; if (mItemArray[index] == null) mItemArray[index] = new Item(); mItemArray[index].size = v; if (copyNow) { mIOBuffer.reset(index * Item.sizeof + 8); mIOBuffer.addF32(v); FieldPacker fp = new FieldPacker(4); fp.addF32(v); mAllocation.setFromFieldPacker(index, 1, fp); } } public Float2 get_position(int index) { if (mItemArray == null) return null; return mItemArray[index].position; } public float get_size(int index) { if (mItemArray == null) return 0; return mItemArray[index].size; } public void copyAll() { for (int ct = 0; ct < mItemArray.length; ct++) copyToArray(mItemArray[ct], ct); mAllocation.setFromFieldPacker(0, mIOBuffer); } public void resize(int newSize) { if (mItemArray != null) { int oldSize = mItemArray.length; int copySize = Math.min(oldSize, newSize); if (newSize == oldSize) return; Item ni[] = new Item[newSize]; System.arraycopy(mItemArray, 0, ni, 0, copySize); mItemArray = ni; } mAllocation.resize(newSize); if (mIOBuffer != null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */); } }
El código generado se te proporciona como una ventaja para asignar memoria a structs solicitadas por el entorno de ejecución de RenderScript e interactuar con struct
s en la memoria. Cada clase de struct
define los siguientes métodos y constructores:
- Constructores sobrecargados que te permiten asignar memoria. El constructor
ScriptField_struct_name(RenderScript rs, int count)
te permite definir la cantidad de estructuras a las que deseas asignar memoria con el parámetrocount
. El constructorScriptField_struct_name(RenderScript rs, int count, int usages)
define un parámetro adicional,usages
, que te permite especificar el espacio de memoria de esta asignación de memoria. Hay cuatro posibilidades de espacio de memoria:USAGE_SCRIPT
: asigna en el espacio de memoria de la secuencia de comandos. Este es el espacio de memoria predeterminado si no especificas un espacio de memoria.USAGE_GRAPHICS_TEXTURE
asigna en el espacio de memoria de textura de la GPU.USAGE_GRAPHICS_VERTEX
asigna en el espacio de memoria de vértice de la GPU.USAGE_GRAPHICS_CONSTANTS
asigna en el espacio de memoria de constantes de la GPU que utilizan los diversos objetos del programa.
Puedes especificar varios espacios de memoria mediante el operador de bits
OR
. De esa manera, se le notifica al entorno de ejecución de RenderScript que vas a acceder a los datos en los espacios de memoria especificados. En el siguiente ejemplo, se asigna memoria a un tipo de datos personalizado en los espacios de memoria de vértice y de secuencia de comandos:Kotlin
val touchPoints: ScriptField_Point = ScriptField_Point( myRenderScript, 2, Allocation.USAGE_SCRIPT or Allocation.USAGE_GRAPHICS_VERTEX )
Java
ScriptField_Point touchPoints = new ScriptField_Point(myRenderScript, 2, Allocation.USAGE_SCRIPT | Allocation.USAGE_GRAPHICS_VERTEX);
- Una clase anidada estática,
Item
, te permite crear una instancia destruct
en forma de objeto. Esta clase anidada es útil si prefieres trabajar construct
en tu código de Android. Cuando hayas terminado de manipular el objeto, podrás enviar el objeto a la memoria asignada llamando aset(Item i, int index, boolean copyNow)
y estableciendoItem
en la posición deseada en el arreglo. El entorno de ejecución de RenderScript automáticamente tiene acceso a la memoria recién escrita. - Métodos de acceso para obtener y establecer los valores de cada campo en una struct. Cada uno de estos métodos de acceso tiene un parámetro
index
para especificarstruct
en el arreglo en el que deseas leer o escribir. Cada método set también tiene un parámetrocopyNow
que especifica si se debe sincronizar inmediatamente esta memoria con el entorno de ejecución de RenderScript. Para sincronizar cualquier memoria que no se haya sincronizado, llama alcopyAll()
. - El método
createElement()
crea una descripción de la struct en la memoria. Esta descripción se utiliza para asignar memoria que consta de uno o más elementos. resize()
funciona de manera muy similar arealloc()
en C, lo que te permite expandir la memoria asignada previamente y mantener los valores actuales que se crearon antes.copyAll()
sincroniza la memoria que se estableció en el nivel de framework con el entorno de ejecución de RenderScript. Cuando llamas a un método set de acceso en un miembro, hay un parámetro booleanocopyNow
opcional que puedes especificar. Si especificastrue
, se sincroniza la memoria cuando llamas al método. Si especificas falso, puedes llamar una vez acopyAll()
, que sincroniza la memoria para todas las propiedades que no están sincronizadas aún.
Punteros
Los punteros globales se reflejan en la propia clase de secuencia de comandos, ubicada en project_root/gen/package/name/ScriptC_renderscript_filename
. Puedes declarar punteros a struct
o cualquiera de los tipos de RenderScript admitidos, pero una struct
no puede incluir punteros ni arreglos anidados. Por ejemplo, si defines los siguientes punteros para una struct
y para int32_t
typedef struct Point { float2 position; float size; } Point_t; Point_t *touchPoints; int32_t *intPointer;
se genera el siguiente código Java:
private ScriptField_Point mExportVar_touchPoints; public void bind_touchPoints(ScriptField_Point v) { mExportVar_touchPoints = v; if (v == null) bindAllocation(null, mExportVarIdx_touchPoints); else bindAllocation(v.getAllocation(), mExportVarIdx_touchPoints); } public ScriptField_Point get_touchPoints() { return mExportVar_touchPoints; } private Allocation mExportVar_intPointer; public void bind_intPointer(Allocation v) { mExportVar_intPointer = v; if (v == null) bindAllocation(null, mExportVarIdx_intPointer); else bindAllocation(v, mExportVarIdx_intPointer); } public Allocation get_intPointer() { return mExportVar_intPointer; }
Se generan un método get
y un método especial llamado bind_pointer_name
(en lugar de un método set()
). El método bind_pointer_name
te permite vincular la memoria asignada en la VM de Android al entorno de ejecución de RenderScript (no puedes asignar memoria en tu archivo .rs
). Para obtener más información, consulta Cómo trabajar con memoria asignada.
API de asignación de memoria
Las aplicaciones que usan RenderScript igual se ejecutan en la VM de Android. Sin embargo, el código real de RenderScript se ejecuta de forma nativa y requiere acceso a la memoria asignada en la VM de Android. Para lograr esto, debes conectar la memoria que está asignada en la VM al entorno de ejecución de RenderScript. Este proceso, denominado vinculación, permite que el entorno de ejecución de RenderScript funcione sin problemas con la memoria que solicita, pero que no puede asignar explícitamente. El resultado final es en esencia el mismo que si hubieras llamado a malloc
en C. El beneficio adicional es que la VM de Android puede llevar a cabo la recolección de elementos no utilizados y compartir la memoria con la capa de entorno de ejecución de RenderScript. La vinculación solo es necesaria para la memoria asignada de forma dinámica. La memoria asignada de manera estática se crea automáticamente para el código de RenderScript en el momento de la compilación. Consulta la figura 1 para obtener más información sobre cómo se realiza la asignación de memoria.
Para admitir este sistema de asignación de memoria, hay un conjunto de API que permiten que la VM de Android asigne memoria y proporcione una funcionalidad similar a una llamada malloc
. Estas clases básicamente describen cómo se debe asignar la memoria y también llevan a cabo la asignación. Para comprender mejor cómo funcionan esas clases, es útil analizarlas en relación con una llamada malloc
simple que puede ser así:
array = (int *)malloc(sizeof(int)*10);
La llamada malloc
se puede dividir en dos partes: el tamaño de la memoria que se asigna (sizeof(int)
) junto con la cantidad de unidades de esa memoria que se deben asignar (10). El framework de Android proporciona clases para esas dos partes, así como una clase para representar el malloc
.
La clase Element
representa la parte (sizeof(int)
) de la llamada malloc
y encapsula una celda de una asignación de memoria, como un valor flotante único o una struct. La clase Type
encapsula el Element
y la cantidad de elementos para asignar (10 en nuestro ejemplo). Puedes entender un Type
como un arreglo de Element
. La clase Allocation
realiza la asignación de memoria real en función de un Type
determinado y representa la memoria asignada real.
En la mayoría de las situaciones, no es necesario llamar a estas API de asignación de memoria directamente. Las clases de capa reflejada generan código para usar estas API automáticamente, y todo lo que necesitas hacer para asignar memoria es llamar a un constructor que esté declarado en una de las clases de capa reflejada y vincular la memoria resultante Allocation
a RenderScript.
Hay algunas situaciones en las que querrías usar esas clases directamente para asignar memoria por tu cuenta, como para cargar un mapa de bits desde un recurso o asignar memoria para punteros a tipos primitivos. En la sección Cómo asignar y vincular la memoria a RenderScript, puedes consultar cómo hacerlo.
En la siguiente tabla, se describen con más detalle las tres clases de administración de la memoria:
Tipo Object de Android | Descripción |
---|---|
Element |
Un elemento describe una celda de una asignación de memoria y puede ser de dos formas: básico o complejo. Un elemento básico incluye un único componente de datos de cualquier tipo de datos de RenderScript válido.
Algunos ejemplos de tipos de datos de elementos básicos son un único valor Los elementos complejos tienen una lista de elementos básicos y se crean a partir de |
Type |
Un tipo es una plantilla de asignación de memoria y tiene un elemento y una o más dimensiones. Describe el diseño de la memoria (básicamente un arreglo de Un tipo consta de cinco dimensiones: X, Y, Z, LOD (nivel de detalle) y caras (de un mapa de cubo). Puedes establecer las dimensiones X, Y y Z en cualquier valor entero positivo dentro de las restricciones de la memoria disponible. Una asignación de una sola dimensión tiene una dimensión X mayor que cero, mientras que las dimensiones Y y Z son cero para indicar que no están presentes. Por ejemplo, una asignación de x=10, y=1 se considera bidimensional y x=10, y=0 se considera unidimensional. Las dimensiones LOD y caras son booleanos que indican si están o no presentes. |
Allocation |
Una asignación proporciona la memoria para las aplicaciones según una descripción de la memoria que se representa con un Los datos de asignación se cargan de una de dos formas principales: tipo marcado y tipo desmarcado.
Para los arreglos simples, hay funciones |
Cómo trabajar con memoria
Las variables globales no estáticas que declaras en tu RenderScript tienen memoria asignada en el momento de la compilación.
Puedes trabajar con estas variables directamente en tu código de RenderScript sin tener que asignarles memoria en el nivel del framework de Android. La capa del framework de Android también tiene acceso a estas variables con los métodos de acceso proporcionados que se generan en las clases de capa reflejada. Si estas variables se inicializan en la capa de entorno de ejecución de RenderScript, esos valores se utilizan para inicializar los valores correspondientes en la capa del framework de Android. Si las variables globales se marcan como const, no se genera un método set
. Busca más detalles aquí.
Nota: Si utilizas ciertas estructuras de RenderScript que incluyen punteros, como rs_program_fragment
y rs_allocation
, primero debes obtener un objeto de la clase del framework de Android correspondiente y, luego, llamar al método set
para que esa estructura vincule la memoria al entorno de ejecución de RenderScript. No puedes manipular directamente estas estructuras en la capa de entorno de ejecución de RenderScript. Esta restricción no se aplica a las estructuras definidas por el usuario que contienen punteros, porque no pueden exportarse a una clase de capa reflejada en primer lugar. Se genera un error de compilador si intentas declarar una struct global no estática que contiene un puntero.
RenderScript también admite punteros, pero debes asignar explícitamente la memoria en el código del framework de Android. Cuando declaras un puntero global en tu archivo .rs
, asignas memoria por medio de la clase de capa reflejada apropiada y vinculas esa memoria a la capa RenderScript nativa. Puedes interactuar con esta memoria desde la capa del framework de Android, así como desde la capa de RenderScript, que te ofrece la flexibilidad para modificar las variables en la capa más adecuada.
Cómo asignar y vincular memoria dinámica al RenderScript
Para asignar memoria dinámica, debes llamar al constructor de una clase Script.FieldBase
; esa es la forma más común. Una alternativa es crear una Allocation
manualmente, lo cual es necesario para los punteros de tipo primitivo, entre otros elementos. Debes usar un constructor de clase Script.FieldBase
siempre que esté disponible a fin de simplificar el proceso.
Después de obtener una asignación de memoria, llama al método bind
reflejado del puntero para vincular la memoria asignada al entorno de ejecución de RenderScript.
En el ejemplo incluido a continuación, se asigna memoria a un puntero de tipo primitivo, intPointer
, y un puntero a una struct, touchPoints
. También se vincula la memoria al RenderScript:
Kotlin
private lateinit var myRenderScript: RenderScript private lateinit var script: ScriptC_example private lateinit var resources: Resources public fun init(rs: RenderScript, res: Resources) { myRenderScript = rs resources = res // allocate memory for the struct pointer, calling the constructor val touchPoints = ScriptField_Point(myRenderScript, 2) // Create an element manually and allocate memory for the int pointer val intPointer: Allocation = Allocation.createSized(myRenderScript, Element.I32(myRenderScript), 2) // create an instance of the RenderScript, pointing it to the bytecode resource script = ScriptC_point(myRenderScript/*, resources, R.raw.example*/) // bind the struct and int pointers to the RenderScript script.bind_touchPoints(touchPoints) script.bind_intPointer(intPointer) ... }
Java
private RenderScript myRenderScript; private ScriptC_example script; private Resources resources; public void init(RenderScript rs, Resources res) { myRenderScript = rs; resources = res; // allocate memory for the struct pointer, calling the constructor ScriptField_Point touchPoints = new ScriptField_Point(myRenderScript, 2); // Create an element manually and allocate memory for the int pointer intPointer = Allocation.createSized(myRenderScript, Element.I32(myRenderScript), 2); // create an instance of the RenderScript, pointing it to the bytecode resource script = new ScriptC_example(myRenderScript, resources, R.raw.example); // bind the struct and int pointers to the RenderScript script.bind_touchPoints(touchPoints); script.bind_intPointer(intPointer); ... }
Cómo leer y escribir en la memoria
Puedes leer y escribir en la memoria asignada de manera estática y dinámica tanto en el entorno de ejecución de RenderScript como en la capa del framework de Android.
La memoria asignada estáticamente viene con una restricción de comunicación unidireccional en el nivel del entorno de ejecución de RenderScript. Cuando el código de RenderScript cambia el valor de una variable, no se lo comunica a la capa del framework de Android por motivos de eficiencia. El último valor que se establece desde el framework de Android siempre se muestra durante una llamada a un método get
. Sin embargo, cuando el código de framework de Android modifica una variable, ese cambio puede comunicarse al entorno de ejecución de RenderScript automáticamente o sincronizarse en otro momento. Si necesitas enviar datos desde el entorno de ejecución de RenderScript a la capa del framework de Android, puedes usar la función rsSendToClient()
para superar esta limitación.
Cuando trabajas con memoria asignada dinámicamente, cualquier cambio en la capa de entorno de ejecución de RenderScript se propaga a la capa del framework de Android si modificaste la asignación de memoria con su puntero asociado. La modificación de un objeto en la capa del framework de Android propaga de inmediato ese cambio a la capa del entorno de ejecución de RenderScript.
Cómo leer y escribir en variables globales
Leer y escribir en variables globales es un proceso sencillo. Puedes usar los métodos de acceso en el nivel del framework de Android o configurarlos directamente en el código de RenderScript. Ten en cuenta que los cambios que realices en el código de RenderScript no se propagarán a la capa del framework de Android (obtén más información aquí).
Por ejemplo, dada la siguiente struct declarada en un archivo llamado rsfile.rs
:
typedef struct Point { int x; int y; } Point_t; Point_t point;
Puedes asignar valores a la struct de esa forma directamente en rsfile.rs
. Estos valores no se propagan al nivel del framework de Android:
point.x = 1; point.y = 1;
Puedes asignar valores a la struct en la capa del framework de Android de esta manera. Estos valores se propagan nuevamente al nivel de entorno de ejecución de RenderScript de forma asíncrona:
Kotlin
val script: ScriptC_rsfile = ... ... script._point = ScriptField_Point.Item().apply { x = 1 y = 1 }
Java
ScriptC_rsfile script; ... Item i = new ScriptField_Point.Item(); i.x = 1; i.y = 1; script.set_point(i);
Puedes leer los valores en tu código de RenderScript de la siguiente manera:
rsDebug("Printing out a Point", point.x, point.y);
Puedes leer los valores en la capa del framework de Android con el siguiente código. Ten en cuenta que este código solo muestra un valor si se configuró en el nivel del framework de Android. Obtendrás una excepción de puntero nulo si solo estableces el valor en el nivel del entorno de ejecución de RenderScript:
Kotlin
Log.i("TAGNAME", "Printing out a Point: ${mScript._point.x} ${mScript._point.y}") println("${point.x} ${point.y}")
Java
Log.i("TAGNAME", "Printing out a Point: " + script.get_point().x + " " + script.get_point().y); System.out.println(point.get_x() + " " + point.get_y());
Cómo leer y escribir punteros globales
Si suponemos que se asignó memoria en el nivel del framework de Android y se vinculó al entorno de ejecución de RenderScript, puedes leer y escribir memoria desde el nivel del framework de Android utilizando los métodos get
y set
para ese puntero.
En la capa del entorno de ejecución de RenderScript, puedes leer y escribir en la memoria con punteros de forma normal y los cambios se propagan a la capa del framework de Android, a diferencia de lo que ocurre con la memoria asignada estáticamente.
Por ejemplo, dado el siguiente puntero a una struct
en un archivo llamado rsfile.rs
:
typedef struct Point { int x; int y; } Point_t; Point_t *point;
Si ya asignaste memoria en la capa del framework de Android, puedes acceder a los valores en struct
de manera normal. Cualquier cambio que realices en la struct a través de su variable de puntero estará automáticamente disponible para la capa del framework de Android:
Kotlin
point[index].apply { x = 1 y = 1 }
Java
point[index].x = 1; point[index].y = 1;
También puedes leer y escribir valores en el puntero de la capa del framework de Android:
Kotlin
val i = ScriptField_Point.Item().apply { x = 100 y = 100 } val p = ScriptField_Point(rs, 1).apply { set(i, 0, true) } script.bind_point(p) p.get_x(0) //read x and y from index 0 p.get_y(0)
Java
ScriptField_Point p = new ScriptField_Point(rs, 1); Item i = new ScriptField_Point.Item(); i.x=100; i.y = 100; p.set(i, 0, true); script.bind_point(p); p.get_x(0); //read x and y from index 0 p.get_y(0);
Una vez que la memoria esté vinculada, no tendrás que volver a vincularla al entorno de ejecución de RenderScript cada vez que realices un cambio en un valor.