Como os apps que usam o RenderScript ainda são executados na VM do Android, você tem acesso a todas as APIs de framework que já conhece, mas pode utilizar o RenderScript quando necessário. Para facilitar essa interação entre o framework e o ambiente de execução do RenderScript, uma camada intermediária de código também está presente para facilitar a comunicação e o gerenciamento de memória entre os dois níveis de código. Este documento aborda mais detalhes sobre essas diferentes camadas de código, além de como a memória é compartilhada entre a VM do Android e o ambiente de execução do RenderScript.
Camada do ambiente de execução do RenderScript
O código do RenderScript é compilado e executado em uma camada do ambiente de execução compacta e bem definida. As APIs do ambiente de execução do RenderScript oferecem suporte para computação intensiva que é portátil e pode ser escalonada automaticamente de acordo com a quantidade de núcleos disponíveis em um processador.
Observação: as funções C padrão no NDK precisam ser executadas em uma CPU. Portanto, o RenderScript não pode acessar essas bibliotecas, porque o RenderScript foi desenvolvido para ser executado em diferentes tipos de processador.
Você define o código do RenderScript nos arquivos .rs
e .rsh
do diretório src/
do seu projeto Android. O código
é compilado para o bytecode intermediário pelo
compilador llvm
que é executado como parte de um build do Android. Quando seu app
é executado em um dispositivo, o bytecode é compilado (just-in-time) para o código de máquina por outro
compilador llvm
que reside no dispositivo. O código de máquina é otimizado para o
dispositivo e também armazenado em cache, de modo que os usos subsequentes do app compatível com RenderScript não
recompilam o bytecode.
Alguns dos principais recursos das bibliotecas de ambiente de execução do RenderScript incluem:
- recursos de solicitação de alocação de memória;
- uma grande coleção de funções matemáticas com versões sobrecarregadas de variáveis escalares e vetoriais de muitas rotinas comuns; operações como adição, multiplicação, produto escalar e produto vetorial estão disponíveis, assim como funções aritméticas e de comparação atômicas;
- rotinas de conversão para tipos e vetores de dados primitivos, rotinas de matriz e rotinas de data e hora;
- tipos de dados e estruturas compatíveis com o sistema RenderScript, por exemplo, tipos vetoriais, para definir dois, três ou quatro vetores;
- funções de registro.
Consulte a referência da API do ambiente de execução do RenderScript para saber mais sobre as funções disponíveis.
Camada refletida
A camada refletida é um conjunto de classes que as ferramentas de criação do Android geram para permitir acesso ao ambiente de execução do RenderScript a partir do framework do Android. Essa camada também fornece métodos e construtores que permitem alocar e trabalhar com memória para ponteiros que são definidos no código do RenderScript. A lista a seguir descreve os principais componentes que são refletidos.
- Todo arquivo
.rs
que você cria é gerado em uma classe chamadaproject_root/gen/package/name/ScriptC_renderscript_filename
do tipoScriptC
. Esse arquivo é a versão.java
do seu arquivo.rs
, que você pode chamar a partir do framework do Android. Essa classe contém os seguintes itens refletidos do arquivo.rs
:- Funções não estáticas
- Variáveis globais não estáticas do RenderScript. Os métodos
do acessador são gerados para cada variável, de modo que você pode ler e
gravar variáveis do RenderScript a partir do framework
do Android. Se uma variável global for inicializada na
camada do ambiente de execução do RenderScript, esses valores serão usados
para inicializar os valores correspondentes na camada de framework
do Android. Se as variáveis globais estiverem marcadas como
const
, um métodoset
não será gerado. Veja aqui para saber mais detalhes. - Ponteiros globais
- Um
struct
é refletido na própria classe chamadaproject_root/gen/package/name/ScriptField_struct_name
, que estendeScript.FieldBase
. Essa classe representa uma matriz destruct
, que permite alocar memória para uma ou mais instâncias dessestruct
.
Funções
As funções são refletidas na própria classe de script, localizada em
project_root/gen/package/name/ScriptC_renderscript_filename
. Por
exemplo, se você definir a seguinte função no código do 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; }
o seguinte código Java será gerado:
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); }
As funções não podem ter valores de retorno porque o sistema RenderScript é projetado para ser assíncrono. Quando o código do framework do Android é chamado para o RenderScript, a chamada é colocada na fila e executada quando possível. Essa restrição permite que o sistema RenderScript funcione sem interrupção constante e aumenta a eficiência. Se as funções pudessem ter valores de retorno, a chamada seria bloqueada até que o valor fosse retornado.
Se você quiser que o código do RenderScript envie um valor de volta ao framework do Android, use a função
rsSendToClient()
.
Variáveis
As variáveis de tipos compatíveis são refletidas na própria classe de script, localizada em
project_root/gen/package/name/ScriptC_renderscript_filename
. Um conjunto de métodos
do acessador é gerado para cada variável. Por exemplo, se você definir a seguinte variável no
código do RenderScript:
uint32_t unsignedInteger = 1;
o seguinte código Java será gerado:
private long mExportVar_unsignedInteger; public void set_unsignedInteger(long v){ mExportVar_unsignedInteger = v; setVar(mExportVarIdx_unsignedInteger, v); } public long get_unsignedInteger(){ return mExportVar_unsignedInteger; }
Estruturas
As estruturas são refletidas em suas próprias classes, localizadas em
<project_root>/gen/com/example/renderscript/ScriptField_struct_name
. Essa
classe representa uma matriz de struct
e permite que você aloque memória para um
número especificado de struct
s. Por exemplo, se você definir a seguinte estrutura:
typedef struct Point { float2 position; float size; } Point_t;
o seguinte código será gerado em 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 */); } }
O código gerado é fornecido como uma conveniência para alocar memória para estruturas solicitadas
pelo ambiente de execução do RenderScript e para interagir com struct
s
na memória. Cada classe de struct
define os seguintes métodos e construtores:
- Construtores sobrecarregados que permitem alocar memória. O
construtor
ScriptField_struct_name(RenderScript rs, int count)
permite que você defina o número de estruturas para as quais quer alocar memória com o parâmetrocount
. O construtorScriptField_struct_name(RenderScript rs, int count, int usages)
define um parâmetro extra,usages
, que permite especificar o espaço de memória dessa alocação de memória. Há quatro possibilidades de espaço de memória:USAGE_SCRIPT
: aloca no espaço de memória do script. Este será o espaço de memória padrão se você não especificar um espaço de memória.USAGE_GRAPHICS_TEXTURE
: aloca no espaço de memória da textura da GPU.USAGE_GRAPHICS_VERTEX
: aloca no espaço de memória de vértice da GPU.USAGE_GRAPHICS_CONSTANTS
: aloca no espaço de memória das constantes da GPU usado pelos vários objetos do programa.
Você pode especificar vários espaços de memória usando o operador
OR
bit a bit. Isso notifica o ambiente de execução do RenderScript que você pretende acessar os dados nos espaços de memória especificados. O exemplo a seguir aloca memória para um tipo de dados personalizado nos espaços de memória de script e vértice: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);
- Uma classe aninhada estática,
Item
, permite que você crie uma instância destruct
, na forma de um objeto. Essa classe aninhada é útil se faz mais sentido trabalhar comstruct
no código do Android. Quando terminar de manipular o objeto, você poderá enviar o objeto para a memória alocada chamandoset(Item i, int index, boolean copyNow)
e definindo oItem
como a posição desejada na matriz. O ambiente de execução do RenderScript tem acesso automático à memória recém-criada. - Métodos do acessador para ver e definir os valores de cada campo em uma estrutura. Cada um desses
métodos do acessador tem um parâmetro
index
para especificar ostruct
na matriz que você quer ler ou gravar. Cada método setter também tem um parâmetrocopyNow
que especifica se a memória precisa ser sincronizada imediatamente com o ambiente de execução do RenderScript. Para sincronizar qualquer memória que não tenha sido sincronizada, chamecopyAll()
. - O método
createElement()
cria uma descrição da estrutura na memória. Essa descrição é usada para alocar memória que consiste em um ou mais elementos. resize()
funciona como umrealloc()
em C, o que permite expandir a memória alocada anteriormente, mantendo os valores atuais que foram criados antes.copyAll()
sincroniza a memória que foi definida no nível do framework com o ambiente de execução do RenderScript. Quando você chama um método do acessador definido em um membro, há um parâmetro booleanocopyNow
opcional que pode ser especificado. Especificartrue
sincroniza a memória quando você chama o método. Se você especificar "falso", poderá chamarcopyAll()
uma vez e sincronizará a memória para todas as propriedades que ainda não estiverem sincronizadas.
Ponteiros
Ponteiros globais são refletidos na própria classe de script, localizada em
project_root/gen/package/name/ScriptC_renderscript_filename
. Você
pode declarar ponteiros para um struct
ou qualquer um dos tipos de RenderScript compatíveis, mas um
struct
não pode conter ponteiros ou matrizes aninhadas. Por exemplo, se você definir os
seguintes ponteiros para struct
e int32_t
,
typedef struct Point { float2 position; float size; } Point_t; Point_t *touchPoints; int32_t *intPointer;
o seguinte código Java será gerado:
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; }
Um método get
e um método especial chamado bind_pointer_name
(em vez de um método set()
) são gerados. O método bind_pointer_name
permite que você vincule a memória
alocada na VM do Android ao ambiente de execução do RenderScript (não é possível alocar
memória no arquivo .rs
). Para saber mais, consulte Como trabalhar com memória alocada.
APIs de alocação de memória
Apps que usam o RenderScript ainda são executados na VM do Android. No entanto, o código do RenderScript real é executado nativamente e
precisa de acesso à memória alocada na VM do Android. Para fazer isso, anexe
a memória alocada na VM ao ambiente de execução do RenderScript. Esse
processo, chamado de "vinculação", permite que o ambiente de execução do RenderScript funcione perfeitamente com a memória
solicitada, mas não pode alocar explicitamente. O resultado final é basicamente o mesmo que se você tivesse
chamado malloc
em C. O benefício adicional é que a VM do Android pode realizar a coleta de lixo e também
compartilhar a memória com a camada de ambiente de execução do RenderScript. A vinculação só é necessária para a memória alocada dinamicamente. A memória
alocada estaticamente é criada automaticamente para o código do RenderScript no momento da compilação. Veja a Figura 1
para saber mais sobre como ocorre a alocação de memória.
Para oferecer compatibilidade com esse sistema de alocação de memória, um conjunto de APIs permite que a VM do Android
aloque memória e ofereça funcionalidade semelhante a uma chamada de malloc
. Basicamente,
essas classes descrevem como a memória precisa ser alocada e também realizam a alocação. Para entender
melhor como essas classes funcionam, é útil pensar nelas em relação a uma chamada
de malloc
simples que pode ter esta aparência:
array = (int *)malloc(sizeof(int)*10);
A chamada de malloc
pode ser dividida em duas partes: o tamanho da memória alocada (sizeof(int)
)
e o total de unidades da memória que precisam ser alocadas (10). O framework do Android fornece classes para essas duas partes,
bem como uma classe para representar malloc
.
A classe Element
representa a parte (sizeof(int)
)
da chamada de malloc
e encapsula uma célula de uma alocação de memória, por exemplo, um único
valor flutuante ou uma estrutura. A classe Type
encapsula a classe Element
e a quantidade de elementos a serem alocados (10 no nosso exemplo). Você pode pensar em Type
como
uma matriz de Element
s. A classe Allocation
realiza a alocação
real de memória com base em um determinado Type
e representa a memória real alocada.
Na maioria das situações, você não precisa chamar essas APIs de alocação de memória diretamente. As classes de camada
refletidas geram código para usar essas APIs automaticamente. Tudo o que você precisa fazer para alocar memória é chamar um
construtor declarado em uma das classes de camada refletidas e vincular
a memória resultante Allocation
ao RenderScript.
Há algumas situações em que convém usar essas classes diretamente para alocar memória por conta
própria, por exemplo, para carregar um bitmap de um recurso ou quando você quer alocar memória para ponteiros
de tipos primitivos. Veja como fazer isso na seção
Como alocar e vincular memória ao RenderScript.
A tabela a seguir descreve as três classes de gerenciamento de memória em mais detalhes:
Tipo de objeto Android | Descrição |
---|---|
Element |
Um elemento descreve uma célula de uma alocação de memória e pode ter duas formas: básica ou complexa. Um elemento básico contém um único componente de dados de qualquer tipo de dados válido do RenderScript.
Exemplos de tipos de dados de elementos básicos incluem um valor Elementos complexos contêm uma lista de elementos básicos e são criados a partir de |
Type |
Um tipo é um modelo de alocação de memória e consiste em um elemento e uma ou mais
dimensões. Ele descreve o layout da memória (basicamente uma matriz de Um tipo consiste em cinco dimensões: X, Y, Z, LOD (nível de detalhe) e faces (de um mapa de cubos). Você pode definir as dimensões X, Y, Z como qualquer valor inteiro positivo dentro das restrições da memória disponível. Uma alocação de dimensão única tem uma dimensão X maior que zero, enquanto as dimensões Y e Z são zero para indicar que não estão presentes. Por exemplo, uma alocação de x=10, y=1 é considerada bidimensional, e x=10, y=0 é considerada unidimensional. As dimensões LOD e Faces são booleanas para indicar que estão presentes ou não. |
Allocation |
Uma alocação fornece a memória para apps com base em uma descrição da memória
que é representada por um Os dados de alocação são enviados de duas maneiras principais: tipo marcado e desmarcado.
Para matrizes simples, há funções de |
Como trabalhar com memória
As variáveis globais não estáticas que você declara no RenderScript são alocadas na memória no momento da compilação.
Você pode trabalhar com essas variáveis diretamente no código do RenderScript sem ter que alocar
memória para elas no nível do framework do Android. A camada do framework do Android também tem acesso a essas variáveis
com os métodos do acessador fornecidos que são gerados nas classes de camada refletidas. Se essas variáveis forem
inicializadas na camada de ambiente de execução do RenderScript, esses valores serão usados para inicializar os valores
correspondentes na camada do framework do Android. Se as variáveis globais forem marcadas como constantes, um set
não
será gerado. Veja mais detalhes aqui.
Observação: se você estiver usando algumas estruturas do RenderScript que contenham ponteiros, como
rs_program_fragment
e rs_allocation
, primeiro será necessário acessar um objeto da classe de framework do Android correspondente e, depois, chamar o método set
para essa
estrutura para vincular a memória ao ambiente de execução do RenderScript. Não é possível manipular diretamente essas estruturas
na camada de ambiente de execução do RenderScript. Essa restrição não é aplicável a estruturas definidas pelo usuário
que contêm ponteiros, porque elas não podem ser exportadas para uma classe de camada refletida
em primeiro lugar. Um erro de compilador será gerado se você tentar declarar uma estrutura global não estática
que contém um ponteiro.
O RenderScript também é compatível com ponteiros, mas você precisa alocar explicitamente a memória no
código do framework do Android. Quando você declara um ponteiro global no arquivo .rs
,
aloca memória usando a classe de camada refletida apropriada e vincula essa memória à camada
do RenderScript nativa. Você pode interagir com essa memória na camada do framework do Android, bem como
na camada do RenderScript, o que oferece a flexibilidade de modificar variáveis na camada
mais apropriada.
Como alocar e vincular memória dinâmica ao RenderScript
Para alocar memória dinâmica, você precisa chamar o construtor de uma
classe Script.FieldBase
, que é a maneira mais comum. Uma alternativa é criar um
Allocation
manualmente, o que é necessário, por exemplo, para ponteiros de tipo primitivo. Para simplificar,
use um construtor de classe Script.FieldBase
sempre que estiver disponível.
Depois de fazer uma alocação de memória, chame o método refletido bind
do ponteiro para vincular a memória alocada ao
ambiente de execução do RenderScript.
O exemplo abaixo aloca memória para um ponteiro de tipo primitivo,
intPointer
, e um ponteiro para uma estrutura, touchPoints
. Ele também vincula a memória ao
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); ... }
Como ler e gravar na memória
Você pode ler e gravar em memória alocada estática e dinamicamente no ambiente de execução do RenderScript e na camada do framework do Android.
A memória alocada estaticamente é fornecida com uma restrição de comunicação unidirecional
no nível do ambiente de execução do RenderScript. Quando o código do RenderScript altera o valor de uma variável, a alteração não é
transmitida de volta para a camada do framework do Android para melhorar a eficiência. O último valor
definido no framework do Android é sempre retornado durante uma chamada para um método
get
. No entanto, quando o código do framework do Android modifica uma variável, essa alteração pode ser transmitida
ao ambiente de execução do RenderScript automaticamente ou sincronizada posteriormente. Se você precisar enviar dados
do ambiente de execução do RenderScript para a camada do framework do Android, poderá usar a
função rsSendToClient()
para superar essa limitação.
Ao trabalhar com memória alocada dinamicamente, as alterações na camada de ambiente de execução do RenderScript serão propagadas de volta para a camada do framework do Android se você tiver modificado a alocação de memória usando o ponteiro associado. Modificar um objeto na camada do framework do Android propaga imediatamente essa alteração para a camada de ambiente de execução do RenderScript.
Como ler e gravar em variáveis globais
Ler e gravar em variáveis globais é um processo simples. Você pode usar os métodos do acessador no nível do framework do Android ou defini-los diretamente no código do RenderScript. Lembre-se de que as alterações feitas no código do RenderScript não são propagadas de volta para a camada do framework do Android. Saiba mais detalhes aqui.
Por exemplo, dado o seguinte struct declarado em um arquivo chamado rsfile.rs
:
typedef struct Point { int x; int y; } Point_t; Point_t point;
Você pode atribuir valores a um struct como este diretamente em rsfile.rs
. Esses valores não são
propagados de volta para o nível do framework do Android:
point.x = 1; point.y = 1;
Você pode atribuir valores ao struct na camada do framework do Android da seguinte forma. Esses valores são propagados de volta para o nível do ambiente de execução do RenderScript de forma assí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);
Você pode ler os valores no código do RenderScript da seguinte maneira:
rsDebug("Printing out a Point", point.x, point.y);
Você pode ler os valores na camada do framework do Android com o código a seguir. Lembre-se de que esse código só retornará um valor se um tiver sido definido no nível do framework do Android. Você verá uma exceção de ponteiro nulo se definir apenas o valor no nível do ambiente de execução do 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());
Como ler e gravar ponteiros globais
Supondo que a memória tenha sido alocada no nível do framework do Android e vinculada ao ambiente de execução do RenderScript,
você pode ler e gravar no nível do framework do Android usando os métodos get
e set
para esse ponteiro.
Na camada de ambiente de execução do RenderScript, você pode ler e gravar na memória com ponteiros normalmente, e as alterações são propagadas
de volta para a camada do framework do Android, diferente do que acontece com a memória alocada estaticamente.
Por exemplo, considerando o seguinte ponteiro para um struct
em um arquivo chamado rsfile.rs
:
typedef struct Point { int x; int y; } Point_t; Point_t *point;
Supondo que você já tenha alocado memória na camada do framework do Android, pode acessar valores em
struct
como de costume. Todas as alterações feitas na estrutura pela variável de ponteiro
estão automaticamente disponíveis para a camada do framework do Android:
Kotlin
point[index].apply { x = 1 y = 1 }
Java
point[index].x = 1; point[index].y = 1;
Também é possível ler e gravar valores no ponteiro da camada do framework do 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);
Depois que a memória já estiver vinculada, você não precisará vincular novamente a memória ao ambiente de execução do RenderScript sempre que fizer uma alteração em um valor.