Script di rendering avanzato

Le applicazioni che utilizzano RenderScript continuano a essere eseguite all'interno della VM Android, hai accesso a tutte le API del framework che conosci, ma che utilizzare RenderScript quando opportuno. Per facilitare questa interazione tra il framework e il runtime RenderScript, anche un livello intermedio di codice è presenti per facilitare la comunicazione e la gestione della memoria tra i due livelli di codice. Questo documento approfondisce questi diversi livelli di codice, nonché le modalità di condivisione della memoria tra la VM Android Runtime RenderScript.

Livello di runtime di RenderScript

Il codice RenderScript è compilato e eseguito in un livello di runtime compatto e ben definito. Le API di runtime RenderScript offrono supporto per ad alta intensità di calcolo, portabile e automaticamente scalabile di core disponibili su un processore.

Nota: le funzioni C standard nell'NDK devono essere l'esecuzione su una CPU è garantita, perciò RenderScript non può accedere a queste librerie, poiché RenderScript è progettato per essere eseguito su diversi tipi di processori.

Definisci il tuo codice RenderScript in .rs e .rsh nella directory src/ del tuo progetto Android. Il codice è compilato in bytecode intermedio Compilatore llvm che viene eseguito come parte di una build Android. Quando la tua applicazione viene eseguito su un dispositivo, il bytecode viene quindi compilato (just-in-time) nel codice macchina da un altro Compilatore llvm che risiede sul dispositivo. Il codice macchina è ottimizzato dispositivo e anche memorizzati nella cache, quindi gli utilizzi successivi dell'applicazione abilitata per RenderScript ricompilare il bytecode.

Alcune funzionalità chiave delle librerie di runtime di RenderScript includono:

  • Funzionalità di richiesta di allocazione della memoria
  • Un'ampia raccolta di funzioni matematiche con versioni sovraccaricate sia scalare che vettoriale. di molte routine comuni. Operazioni come l'aggiunta, la moltiplicazione, il prodotto scalare e il prodotto incrociato nonché le funzioni di aritmetica atomica e di confronto.
  • Routine di conversione per tipi e vettori di dati primitivi, routine matriciali e data e ora routine
  • Tipi di dati e strutture per supportare il sistema RenderScript come i tipi Vector per definire due, tre o quattro vettori.
  • Funzioni di logging

Per ulteriori informazioni sulle funzioni disponibili, consulta il riferimento dell'API runtime RenderScript.

Strato riflesso

Il livello riflesso è un insieme di classi generate dagli strumenti di build Android per consentire l'accesso al runtime RenderScript dal framework Android. Questo livello fornisce anche che consentono di allocare e lavorare con la memoria per i puntatori definiti il tuo codice RenderScript. Nell'elenco che segue vengono descritti i principali componenti che si riflettono:

  • Ogni file .rs che crei viene generato in una classe denominata project_root/gen/package/name/ScriptC_renderscript_filename di digita ScriptC. Questo file è la versione .java del tuo .rs, che puoi richiamare dal framework Android. Questo corso contiene seguenti elementi riportati dal file .rs:
    • Funzioni non statiche
    • Variabili RenderScript globali non statiche. Funzione di accesso vengono generati per ogni variabile, in modo che tu possa leggere scrivere le variabili RenderScript dall'interfaccia il modello di machine learning. Se una variabile globale viene inizializzata RenderScript, questi valori vengono utilizzati per inizializzare i valori corrispondenti nel framework Android livello di sicurezza. Se le variabili globali sono contrassegnate come const, un metodo set non è generati. Guarda qui per ulteriori informazioni.

    • Cursori globali
  • Un struct si riflette nella propria classe denominata project_root/gen/package/name/ScriptField_struct_name, che estende Script.FieldBase. Questa classe rappresenta un array struct, che consente di allocare memoria per una o più istanze di questo struct.

Funzioni

Le funzioni si riflettono nella classe script stessa, situata in project_root/gen/package/name/ScriptC_renderscript_filename. Per esempio, se definisci la funzione seguente nel codice 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;
}

viene generato il seguente codice 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);
}

Le funzioni non possono avere valori restituiti perché il sistema RenderScript è progettato per asincrone. Quando il codice del framework Android chiama a RenderScript, la chiamata viene accodata e viene quando è possibile. Questa restrizione consente al sistema RenderScript di funzionare senza e aumenta l'efficienza. Se alle funzioni è consentito avere valori restituiti, la chiamata viene bloccato finché non viene restituito il valore.

Se desideri che il codice RenderScript invii un valore al framework Android, utilizza la classe rsSendToClient() personalizzata.

Variabili

Le variabili dei tipi supportati si riflettono nella classe script stessa, situata in project_root/gen/package/name/ScriptC_renderscript_filename. Un insieme di funzioni di accesso vengono generati per ogni variabile. Ad esempio, se definisci la seguente variabile in il codice RenderScript:

uint32_t unsignedInteger = 1;

viene generato il seguente codice 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

Gli struct si riflettono nelle proprie classi, che si trovano in <project_root>/gen/com/example/renderscript/ScriptField_struct_name. Questo rappresenta un array di struct e consente di allocare memoria per il numero specificato di struct s. Ad esempio, se definisci il seguente struct:

typedef struct Point {
    float2 position;
    float size;
} Point_t;

in ScriptField_Point.java viene generato il seguente codice:

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 */);
    }
}

Il codice generato ti viene fornito per comodità di allocare la memoria per gli struct richiesti dal runtime RenderScript e di interagire con i struct in memoria. Ogni classe di struct definisce i seguenti metodi e costruttori:

  • Costruttori sovraccarichi che consentono di allocare la memoria. La Il costruttore ScriptField_struct_name(RenderScript rs, int count) consente definire il numero di strutture per le quali allocare la memoria con count. Il costruttore ScriptField_struct_name(RenderScript rs, int count, int usages) definisce un parametro aggiuntivo, usages, che consente di specificare lo spazio di memoria dell'allocazione. Lo spazio di memoria è quattro possibili:
    • USAGE_SCRIPT: alloca nella memoria dello script spazio. Questo è lo spazio di memoria predefinito se non specifichi uno spazio di memoria.
    • USAGE_GRAPHICS_TEXTURE: alloca nell'area spazio di memoria della texture della GPU.
    • USAGE_GRAPHICS_VERTEX: l'allocazione nel vertice di memoria della GPU.
    • USAGE_GRAPHICS_CONSTANTS: alloca nell'area spazio di memoria costante della GPU utilizzato dai vari oggetti del programma.

    Puoi specificare più spazi di memoria utilizzando l'operatore OR bit a bit. In questo modo comunica al runtime RenderScript che intendi accedere ai dati in spazi di memoria specificati. L'esempio seguente alloca la memoria per un tipo di dati personalizzato nello script e negli spazi di memoria Vertex:

    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 classe nidificata statica, Item, ti consente di creare un'istanza del struct, sotto forma di oggetto. Questa classe nidificata è utile se ha più senso funzionare con il struct nel tuo codice Android. Quando hai finito di manipolare l'oggetto, puoi eseguire il push dell'oggetto nella memoria allocata chiamando set(Item i, int index, boolean copyNow) e impostando Item sulla posizione desiderata in l'array. Il runtime RenderScript ha automaticamente accesso alla memoria appena scritta.
  • Metodi della funzione di accesso per recuperare e impostare i valori di ciascun campo in uno struct. Ciascuno di questi metodi della funzione di accesso hanno un parametro index per specificare struct in l'array su cui si vuole leggere o scrivere. Ogni metodo di impostazione ha anche Parametro copyNow che specifica se sincronizzare immediatamente questa memoria al runtime RenderScript. Per sincronizzare qualsiasi informazione che non è stata sincronizzata, chiama copyAll().
  • Il metodo createElement() crea una descrizione dello struct in memoria. Questo descrizione viene utilizzata per allocare una memoria composta da uno o più elementi.
  • Il formato resize() funziona in modo molto simile a realloc() in C, permettendoti di espandere la memoria allocata in precedenza, mantenendo i valori attuali che erano in precedenza è stato creato.
  • copyAll() sincronizza la memoria impostata a livello di framework con la Runtime RenderScript. Quando chiami un metodo della funzione di accesso impostato su un membro, è disponibile una classe facoltativa copyNow parametro booleano che puoi specificare. Specificare true sincronizza la memoria quando chiami il metodo. Se specifichi false, puoi chiamare copyAll() una volta e sincronizza la memoria per tutti che non sono ancora state sincronizzate.

Puntatori

I puntatori globali si riflettono nella classe dello script stessa, situata in project_root/gen/package/name/ScriptC_renderscript_filename. Tu può dichiarare i puntatori a un struct o a uno qualsiasi dei tipi di RenderScript supportati, ma struct non può contenere puntatori o array nidificati. Ad esempio, se definisci seguire i puntatori a struct e int32_t

typedef struct Point {
    float2 position;
    float size;
} Point_t;

Point_t *touchPoints;
int32_t *intPointer;

viene generato il seguente codice 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;
}
  

Un metodo get e un metodo speciale denominato bind_pointer_name (anziché un metodo set()). Il metodo bind_pointer_name ti consente di associare la memoria allocato nella VM Android al runtime RenderScript (non puoi allocare memoria nel file .rs). Per ulteriori informazioni, vedi Utilizzo con la memoria allocata.

API di allocazione della memoria

Le applicazioni che utilizzano RenderScript continuano a essere eseguite nella VM Android. L'effettivo codice RenderScript, tuttavia, viene eseguito in modo nativo deve accedere alla memoria allocata nella VM Android. A questo scopo, devi collegare la memoria allocata nella VM al runtime di RenderScript. Questo di processo, detto associazione, consente al runtime RenderScript di lavorare senza problemi con la memoria ma non possono allocare esplicitamente. Il risultato finale è essenzialmente lo stesso che avresti ottenuto chiamata malloc in Do. Il vantaggio aggiuntivo è che la VM Android può eseguire la garbage collection, con il livello di runtime RenderScript. L'associazione è necessaria solo per la memoria allocata dinamicamente. In modo statico la memoria allocata viene creata automaticamente per il codice RenderScript al momento della compilazione. Vedi la Figura 1 per ulteriori informazioni sull'allocazione della memoria.

Per supportare questo sistema di allocazione della memoria, è disponibile una serie di API che consentono alla VM Android di allocano memoria e offrono funzionalità simili a quelle di una chiamata malloc. Questi corsi descrivono essenzialmente come allocare la memoria e anche eseguire l'allocazione. Per migliorare capire come funzionano queste classi, è utile considerarli in relazione a una malloc chiamata che può avere il seguente aspetto:

array = (int *)malloc(sizeof(int)*10);

La chiamata malloc può essere suddivisa in due parti: la dimensione della memoria allocata (sizeof(int)), insieme al numero di unità di quella memoria da allocare (10). Il framework di Android fornisce classi per queste due parti, e una classe che rappresenta malloc stesso.

La classe Element rappresenta la parte (sizeof(int)) della chiamata malloc e incapsula una cella di un'allocazione di memoria, come un in un valore in virgola mobile o in uno struct. La classe Type racchiude il Element e il numero di elementi da allocare (10 nel nostro esempio). Puoi considerare Type come un array di Element. La classe Allocation esegue effettivamente all'allocazione della memoria in base a un determinato Type e rappresenta la memoria allocata effettiva.

Nella maggior parte dei casi, non è necessario chiamare direttamente queste API di allocazione della memoria. Il livello riflesso generano il codice per utilizzare automaticamente queste API. Per allocare la memoria basta chiamare dichiarato in una delle classi di strati riflessi e poi vincolare la memoria risultante Allocation al RenderScript. In alcune situazioni potrebbe essere utile utilizzare direttamente queste classi per allocare la memoria sul come il caricamento di una bitmap da una risorsa o quando si desidera allocare memoria per i puntatori a tipi primitivi. Puoi scoprire come farlo nel Allocazione e associazione della memoria alla sezione RenderScript. Nella tabella seguente vengono descritte in modo più dettagliato le tre classi di gestione della memoria:

Tipo di oggetto Android Descrizione
Element

Un elemento descrive una cella di allocazione di memoria e può avere due forme: di base o complessi.

Un elemento di base contiene un singolo componente dei dati di qualsiasi tipo di dati RenderScript valido. Esempi di tipi di dati degli elementi di base includono un singolo valore float, un vettore float4 o un con un singolo colore RGB-565.

Gli elementi complessi contengono un elenco di elementi di base e vengono creati struct dichiarati nel codice RenderScript. Ad esempio, un'allocazione può contenere più struct disposti in ordine in memoria. Ogni struct viene considerato come anziché ogni tipo di dati all'interno dello struct.

Type

Un tipo è un modello di allocazione della memoria ed è composto da un elemento e da uno o più dimensioni. Descrive il layout della memoria (praticamente un array di Element) ma non alloca la memoria per i dati che che descrive il problema.

Un tipo è costituito da cinque dimensioni: X, Y, Z, livello di dettaglio (livello di dettaglio) e Volti (di un cubo). mappa). Puoi impostare le dimensioni X,Y,Z su qualsiasi valore intero positivo all'interno dei i vincoli della memoria disponibile. L'allocazione a dimensione singola ha una dimensione X di maggiore di zero, mentre le dimensioni Y e Z sono pari a zero per indicare che non è presente. Per Ad esempio, un'allocazione di x=10, y=1 è considerata bidimensionale e x=10, y=0 è sono considerati monodimensionali. Le dimensioni LOD e Volti sono booleani per indicare la presenza di o meno.

Allocation

Un'allocazione fornisce la memoria per le applicazioni in base a una descrizione della memoria rappresentato da un Type. La memoria allocata può esistere in contemporaneamente più spazi di memoria. Se la memoria viene modificata in uno spazio, devi specificare sincronizza la memoria, in modo che venga aggiornata in tutti gli altri spazi in cui si trova.

I dati di allocazione vengono caricati in uno dei due modi principali: tipo selezionato e tipo deselezionato. Per gli array semplici, ci sono funzioni copyFrom() che prendono un array dalla Android e la copia nel datastore del livello nativo. Le varianti non selezionate consentono il sistema Android a copiare array di strutture perché non supporta le nostre strutture. Ad esempio, se esiste un'allocazione composta da un array di n numeri in virgola mobile, i dati contenuto in un array float[n] o in un array byte[n*4] può essere copiato.

Utilizzo della memoria

Le variabili globali non statiche dichiarate in RenderScript sono memoria allocata al momento della compilazione. Puoi lavorare con queste variabili direttamente nel codice RenderScript senza dover allocare a livello di framework Android. Anche il livello del framework Android ha accesso a queste variabili con i metodi della funzione di accesso forniti che vengono generati nelle classi degli strati riflessi. Se queste variabili sono inizializzati al livello di runtime di RenderScript, tali valori vengono utilizzati per inizializzare i corrispondenti nel livello framework Android. Se le variabili globali sono contrassegnate come const, viene utilizzato un metodo set non generati. Per ulteriori dettagli, fai clic qui.

Nota: se utilizzi determinate strutture di RenderScript che contengono puntatori, come rs_program_fragment e rs_allocation, devi ottenere un oggetto la classe framework Android corrispondente, quindi chiama il metodo set. per associare la memoria al runtime di RenderScript. Non puoi manipolare direttamente queste strutture a livello di runtime di RenderScript. Questa limitazione non è applicabile alle strutture definite dall'utente che contengono puntatori, perché non possono essere esportati in una classe di livello riflessa in primo luogo. Se tenti di dichiarare un ambiente globale non statico, viene generato un errore del compilatore che contiene un puntatore.

Anche RenderScript supporta i puntatori, ma devi allocare esplicitamente la memoria nel tuo Codice framework Android. Quando dichiari un puntatore globale nel file .rs, alloca la memoria tramite l'appropriata classe di strato riflessa e associala il livello RenderScript. Puoi interagire con questa memoria dal livello del framework Android, nonché il livello RenderScript, che offre la flessibilità di modificare le variabili livello appropriato.

Allocazione e associazione della memoria dinamica a RenderScript

Per allocare la memoria dinamica, devi chiamare il costruttore di un Script.FieldBase, che è il modo più comune. Un'alternativa è creare un Allocation manualmente, che è necessaria per elementi come i puntatori di tipo primitivi. Dovresti Per semplicità, utilizza un costruttore di classe Script.FieldBase, quando disponibile. Dopo aver ottenuto un'allocazione della memoria, chiama il metodo bind riflesso del puntatore per associare la memoria allocata alla Runtime RenderScript.

L'esempio seguente alloca la memoria per un puntatore di tipo primitivo, intPointer e un puntatore a uno struct, touchPoints. Inoltre, lega la memoria 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);

   ...
}

Lettura e scrittura in memoria

Puoi leggere e scrivere su memoria allocata in modo statico e dinamico sia nel runtime di RenderScript e Android.

La memoria allocata in modo statico è fornita con una limitazione della comunicazione unidirezionale a livello di runtime di RenderScript. Quando il codice RenderScript cambia il valore di una variabile, al livello del framework Android a fini di efficienza. L'ultimo valore impostato dal framework Android viene sempre restituito durante una chiamata a un get . Tuttavia, quando il codice del framework Android modifica una variabile, tale modifica può essere comunicata a il runtime di RenderScript automaticamente o sincronizzato in un secondo momento. Se devi inviare dati dal runtime RenderScript al livello del framework Android, puoi utilizzare Funzione rsSendToClient() per superare questa limitazione.

Quando si lavora con memoria allocata dinamicamente, qualsiasi modifica al livello di runtime di RenderScript viene propagata al livello del framework Android se hai modificato l'allocazione della memoria utilizzando il puntatore associato. La modifica di un oggetto a livello del framework Android propaga immediatamente il cambiamento a RenderScript il livello di runtime.

Lettura e scrittura su variabili globali

La lettura e la scrittura nelle variabili globali è un processo semplice. Puoi usare i metodi della funzione di accesso a livello di framework Android o impostarle direttamente nel codice RenderScript. Tieni presente che ogni le modifiche apportate nel codice RenderScript non vengono propagate torna al livello framework Android (guarda qui per ulteriori informazioni).

Ad esempio, dato il seguente struct dichiarato in un file denominato rsfile.rs:

typedef struct Point {
    int x;
    int y;
} Point_t;

Point_t point;

Puoi assegnare valori allo struct in questo modo direttamente in rsfile.rs. Questi valori non sono propagata di nuovo al livello del framework Android:

point.x = 1;
point.y = 1;

Puoi assegnare valori allo struct a livello del framework Android in questo modo. Questi valori sono propagati di nuovo al livello di runtime di RenderScript in modo asincrono:

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);

Puoi leggere i valori nel codice RenderScript in questo modo:

rsDebug("Printing out a Point", point.x, point.y);

Puoi leggere i valori nel livello del framework Android con il seguente codice. Tieni presente che restituisce un valore solo se ne è stato impostato uno a livello di framework Android. Verrà visualizzato un puntatore nullo se imposti il valore solo al livello di runtime di 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());

Lettura e scrittura di cursori globali

Supponendo che la memoria sia stata allocata a livello del framework Android e legata al runtime di RenderScript, puoi leggere e scrivere la memoria a livello di framework Android utilizzando i metodi get e set per il puntatore. Nel livello di runtime di RenderScript, puoi leggere e scrivere nella memoria con i puntatori come di consueto e le modifiche vengono propagate al livello del framework Android, a differenza della memoria allocata in modo statico.

Ad esempio, dato il seguente puntatore a un struct in un file denominato rsfile.rs:

typedef struct Point {
    int x;
    int y;
} Point_t;

Point_t *point;

Supponendo che tu abbia già allocato la memoria a livello del framework Android, puoi accedere ai valori in struct come di consueto. Qualsiasi modifica apportata allo struct mediante la variabile puntatore sono automaticamente disponibili per il livello framework Android:

Kotlin

point[index].apply {
    x = 1
    y = 1
}

Java

point[index].x = 1;
point[index].y = 1;

Puoi leggere e scrivere valori sul puntatore anche a livello di framework 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);

Quando la memoria è già associata, non è necessario associarla nuovamente a RenderScript ogni volta che modifichi un valore.