Anwendungen, die RenderScript nutzen, werden weiterhin innerhalb der Android-VM ausgeführt. Du hast also Zugriff auf alle Framework-APIs, mit denen du vertraut bist. Du kannst bei Bedarf jedoch RenderScript nutzen. Um diese Interaktion zwischen dem Framework und der RenderScript-Laufzeit zu erleichtern, ist auch eine Zwischenebene mit Code vorhanden, die die Kommunikation und Speicherverwaltung zwischen den beiden Codeebenen erleichtert. In diesem Dokument werden diese verschiedenen Codeebenen ausführlicher beschrieben. Außerdem wird erläutert, wie der Arbeitsspeicher zwischen der Android-VM und der RenderScript-Laufzeit aufgeteilt wird.
RenderScript-Laufzeitebene
Ihr RenderScript-Code wird in einer kompakten und klar definierten Laufzeitebene kompiliert und ausgeführt. Die RenderScript-Laufzeit-APIs unterstützen intensive Berechnungen, die portierbar und automatisch auf die Anzahl der auf einem Prozessor verfügbaren Kerne skaliert werden können.
Hinweis:Die Standard-C-Funktionen im NDK müssen garantiert auf einer CPU ausgeführt werden, damit RenderScript nicht auf diese Bibliotheken zugreifen kann, da RenderScript für verschiedene Prozessortypen entwickelt wurde.
Du definierst den RenderScript-Code in den Dateien .rs
und .rsh
im Verzeichnis src/
deines Android-Projekts. Der Code wird vom llvm
-Compiler, der als Teil eines Android-Builds ausgeführt wird, in Zwischenbytecode kompiliert. Wenn Ihre Anwendung auf einem Gerät ausgeführt wird, wird der Bytecode dann (Just-in-Time) von einem anderen llvm
-Compiler, der sich auf dem Gerät befindet, in Maschinencode kompiliert. Der Maschinencode ist für das Gerät optimiert und auch im Cache gespeichert, sodass bei späteren Verwendungen der RenderScript-fähigen Anwendung der Bytecode nicht neu kompiliert wird.
Zu den wichtigsten Funktionen der RenderScript-Laufzeitbibliotheken gehören:
- Features von Anfragen zur Arbeitsspeicherzuweisung
- Eine große Sammlung von mathematischen Funktionen mit sowohl skalaren als auch vektortypisierten, überlasteten Versionen vieler gängiger Routinen. Es sind Vorgänge wie Addition, Multiplikation, Punktprodukt und Kreuzprodukt sowie Funktionen für atomare Arithmetik und Vergleich verfügbar.
- Konvertierungsroutinen für primitive Datentypen und Vektoren, Matrixroutinen sowie Datums- und Uhrzeitroutinen
- Datentypen und -strukturen zur Unterstützung des RenderScript-Systems, z. B. Vektortypen zum Definieren von zwei, drei oder vier Vektoren
- Logging-Funktionen
Weitere Informationen zu den verfügbaren Funktionen finden Sie in der Referenz zur RenderScript Runtime API.
Reflexionsschicht
Die reflektierte Ebene besteht aus einer Reihe von Klassen, die von den Android-Build-Tools generiert werden, um den Zugriff auf die RenderScript-Laufzeit aus dem Android-Framework zu ermöglichen. Diese Ebene bietet auch Methoden und Konstruktoren, mit denen Sie Arbeitsspeicher für Zeiger zuweisen und mit ihnen arbeiten können, die in Ihrem RenderScript-Code definiert sind. In der folgenden Liste werden die wichtigsten Komponenten beschrieben:
- Jede von Ihnen erstellte
.rs
-Datei wird in einer Klasse namensproject_root/gen/package/name/ScriptC_renderscript_filename
vom TypScriptC
generiert. Dies ist die.java
-Version Ihrer.rs
-Datei, die Sie aus dem Android-Framework aufrufen können. Diese Klasse enthält die folgenden Elemente aus der Datei.rs
:- Nicht-statische Funktionen
- Nicht statische, globale RenderScript-Variablen Für jede Variable werden Zugriffsmethoden generiert, sodass Sie die RenderScript-Variablen aus dem Android-Framework lesen und schreiben können. Wenn eine globale Variable auf der RenderScript-Laufzeitebene initialisiert wird, werden diese Werte verwendet, um die entsprechenden Werte in der Android-Framework-Ebene zu initialisieren. Wenn globale Variablen als
const
gekennzeichnet sind, wird keineset
-Methode generiert. Weitere Informationen - Globale Cursor
- Eine
struct
spiegelt sich in einer eigenen Klasse namensproject_root/gen/package/name/ScriptField_struct_name
wider, dieScript.FieldBase
erweitert. Diese Klasse stellt ein Array vonstruct
dar, mit dem Sie Arbeitsspeicher für eine oder mehrere Instanzen dieserstruct
zuweisen können.
Funktionen
Funktionen werden in der Skriptklasse selbst in project_root/gen/package/name/ScriptC_renderscript_filename
wiedergegeben. Angenommen, Sie definieren die folgende Funktion in Ihrem RenderScript-Code:
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; }
wird der folgende Java-Code generiert:
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); }
Funktionen können keine Rückgabewerte haben, da das RenderScript-System asynchron ausgelegt ist. Wenn Ihr Android-Framework-Code RenderScript aufruft, wird der Aufruf in die Warteschlange gestellt und nach Möglichkeit ausgeführt. Durch diese Einschränkung kann das RenderScript-System ohne ständige Unterbrechung funktionieren und die Effizienz steigern. Wenn für Funktionen Rückgabewerte zulässig wären, wird der Aufruf blockiert, bis der Wert zurückgegeben wurde.
Wenn Sie möchten, dass der RenderScript-Code einen Wert an das Android-Framework zurücksendet, verwenden Sie die Funktion rsSendToClient()
.
Variablen
Variablen unterstützter Typen werden in der Skriptklasse selbst in project_root/gen/package/name/ScriptC_renderscript_filename
wiedergegeben. Für jede Variable wird eine Reihe von Zugriffsmethoden generiert. Angenommen, Sie definieren die folgende Variable in Ihrem RenderScript-Code:
uint32_t unsignedInteger = 1;
wird der folgende Java-Code generiert:
private long mExportVar_unsignedInteger; public void set_unsignedInteger(long v){ mExportVar_unsignedInteger = v; setVar(mExportVarIdx_unsignedInteger, v); } public long get_unsignedInteger(){ return mExportVar_unsignedInteger; }
Strukturen
Structs werden in eigenen Klassen in <project_root>/gen/com/example/renderscript/ScriptField_struct_name
widergespiegelt. Diese Klasse stellt ein Array von struct
dar und ermöglicht Ihnen, Arbeitsspeicher für eine bestimmte Anzahl von struct
s zuzuweisen. Angenommen, Sie definieren die folgende Struktur:
typedef struct Point { float2 position; float size; } Point_t;
wird der folgende Code in ScriptField_Point.java
generiert:
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 */); } }
Der generierte Code wird Ihnen zur Verfügung gestellt, damit Sie Arbeitsspeicher für von der RenderScript-Laufzeit angeforderte Strukturen zuweisen und mit struct
s im Arbeitsspeicher interagieren können. Die Klasse jeder struct
definiert die folgenden Methoden und Konstruktoren:
- Überladene Konstruktoren, mit denen Sie Arbeitsspeicher zuweisen können. Mit dem Konstruktor
ScriptField_struct_name(RenderScript rs, int count)
können Sie mit dem Parametercount
die Anzahl der Strukturen definieren, denen Sie Arbeitsspeicher zuweisen möchten. DerScriptField_struct_name(RenderScript rs, int count, int usages)
-Konstruktor definiert den zusätzlichen Parameterusages
, mit dem Sie den Arbeitsspeicherbereich dieser Arbeitsspeicherzuweisung angeben können. Es gibt vier mögliche Speicherkapazitäten:USAGE_SCRIPT
: Weist den Script-Arbeitsspeicher zu. Dies ist der Standardarbeitsbereich, wenn Sie keinen festlegen.USAGE_GRAPHICS_TEXTURE
: Weist den Textur-Speicherbereich der GPU zu.USAGE_GRAPHICS_VERTEX
: Weist den Vertex-Arbeitsspeicher der GPU zu.USAGE_GRAPHICS_CONSTANTS
: Weist den konstanten Arbeitsspeicher der GPU zu, der von den verschiedenen Programmobjekten verwendet wird.
Sie können mehrere Arbeitsspeicherbereiche mithilfe des bitweisen
OR
-Operators angeben. Dadurch wird die RenderScript-Laufzeit darüber informiert, dass Sie auf die Daten in den angegebenen Arbeitsspeicherbereichen zugreifen möchten. Im folgenden Beispiel wird Arbeitsspeicher für einen benutzerdefinierten Datentyp sowohl im Skript als auch in den Scheitelpunkt-Arbeitsspeicherbereichen zugewiesen: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);
- Mit der statischen, verschachtelten Klasse
Item
können Sie eine Instanz vonstruct
in Form eines Objekts erstellen. Diese verschachtelte Klasse ist nützlich, wenn die Arbeit mitstruct
in Ihrem Android-Code sinnvoller ist. Wenn Sie mit der Bearbeitung des Objekts fertig sind, können Sie das Objekt in den zugewiesenen Speicher verschieben. Rufen Sie dazuset(Item i, int index, boolean copyNow)
auf und setzen SieItem
auf die gewünschte Position im Array. Die RenderScript-Laufzeit hat automatisch Zugriff auf den neu geschriebenen Arbeitsspeicher. - Zugriffsmethoden zum Abrufen und Festlegen der Werte der einzelnen Felder in einer Struktur. Jede dieser Zugriffsmethoden hat einen
index
-Parameter, um diestruct
in dem Array anzugeben, in das Sie lesen oder schreiben möchten. Jede Setter-Methode hat auch einencopyNow
-Parameter, der angibt, ob dieser Arbeitsspeicher sofort mit der RenderScript-Laufzeit synchronisiert werden soll. Um eine Erinnerung zu synchronisieren, die nicht synchronisiert wurde, rufcopyAll()
auf. - Mit der Methode
createElement()
wird eine Beschreibung der Struktur im Speicher erstellt. Diese Beschreibung wird verwendet, um Arbeitsspeicher zuzuweisen, der aus einem oder mehreren Elementen besteht. resize()
funktioniert ähnlich wie einrealloc()
in C. Sie können also zuvor zugewiesenen Arbeitsspeicher erweitern und die aktuellen Werte beibehalten, die zuvor erstellt wurden.copyAll()
synchronisiert den auf Framework-Ebene festgelegten Arbeitsspeicher mit der RenderScript-Laufzeit. Wenn Sie eine Zugriffsmethode für ein Element aufrufen, können Sie den optionalen booleschen ParametercopyNow
angeben. Wenn Sietrue
angeben, wird der Arbeitsspeicher beim Aufrufen der Methode synchronisiert. Wenn Sie „false“ angeben, können SiecopyAll()
einmal aufrufen. Dadurch wird der Arbeitsspeicher für alle Attribute synchronisiert, die noch nicht synchronisiert sind.
Zeiger
Globale Zeiger werden in der Skriptklasse selbst in project_root/gen/package/name/ScriptC_renderscript_filename
wiedergegeben. Sie können Zeiger auf ein struct
oder einen der unterstützten RenderScript-Typen deklarieren, aber ein struct
darf keine Zeiger oder verschachtelten Arrays enthalten. Wenn Sie beispielsweise die folgenden Zeiger auf struct
und int32_t
definieren
typedef struct Point { float2 position; float size; } Point_t; Point_t *touchPoints; int32_t *intPointer;
wird der folgende Java-Code generiert:
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; }
Eine get
-Methode und eine spezielle Methode namens bind_pointer_name
(anstelle einer set()
-Methode) werden generiert. Mit der Methode bind_pointer_name
können Sie den in der Android-VM zugewiesenen Arbeitsspeicher an die RenderScript-Laufzeit binden. Sie können in der Datei .rs
keinen Arbeitsspeicher zuweisen. Weitere Informationen finden Sie unter Mit zugewiesenem Arbeitsspeicher arbeiten.
APIs zur Arbeitsspeicherzuweisung
Anwendungen, die RenderScript verwenden, werden weiterhin auf der Android-VM ausgeführt. Der eigentliche RenderScript-Code wird jedoch nativ ausgeführt und benötigt Zugriff auf den in der Android-VM zugewiesenen Speicher. Dazu müssen Sie den in der VM zugewiesenen Arbeitsspeicher an die RenderScript-Laufzeit anhängen. Dieser Prozess, der als Bindung bezeichnet wird, ermöglicht der RenderScript-Laufzeit, nahtlos mit Arbeitsspeicher zu arbeiten, den sie anfordert, aber nicht explizit zuweisen kann. Das Endergebnis ist im Wesentlichen dasselbe wie bei malloc
in C. Der zusätzliche Vorteil besteht darin, dass die Android-VM die automatische Speicherbereinigung durchführen und Arbeitsspeicher mit der RenderScript-Laufzeitebene teilen kann. Eine Bindung ist nur für dynamisch zugewiesenen Arbeitsspeicher erforderlich. Statisch zugewiesener Speicher wird bei der Kompilierung automatisch für Ihren RenderScript-Code erstellt. Weitere Informationen zur Arbeitsspeicherzuweisung finden Sie in Abbildung 1.
Um dieses Arbeitsspeicherzuweisungssystem zu unterstützen, gibt es eine Reihe von APIs, mit denen die Android-VM Arbeitsspeicher zuweisen kann und die ähnliche Funktionen wie ein malloc
-Aufruf bieten. Diese Klassen beschreiben im Wesentlichen, wie Speicher zugewiesen werden soll, und führen auch die Zuweisung durch. Zum besseren Verständnis der Funktionsweise dieser Klassen empfiehlt es sich, sie in Bezug auf einen einfachen malloc
-Aufruf zu betrachten, der so aussehen kann:
array = (int *)malloc(sizeof(int)*10);
Der malloc
-Aufruf kann in zwei Teile unterteilt werden: die Größe des zugewiesenen Speichers (sizeof(int)
) und die Anzahl der zuzuweisenden Einheiten dieses Speichers (10). Das Android-Framework bietet Klassen für diese beiden Teile sowie eine Klasse, um malloc
selbst darzustellen.
Die Klasse Element
stellt den Teil (sizeof(int)
) des malloc
-Aufrufs dar und kapselt eine Zelle einer Arbeitsspeicherzuweisung, z. B. einen einzelnen Gleitkommawert oder eine Struktur. Die Klasse Type
kapselt den Element
und die Anzahl der zuzuweisenden Elemente (in unserem Beispiel 10). Sie können sich ein Type
als ein Array von Element
-Werten vorstellen. Die Klasse Allocation
führt die tatsächliche Arbeitsspeicherzuweisung anhand einer bestimmten Type
durch und stellt den tatsächlich zugewiesenen Arbeitsspeicher dar.
In den meisten Fällen müssen Sie diese APIs zur Arbeitsspeicherzuweisung nicht direkt aufrufen. Die Klassen der reflektierten Schichten generieren Code zur Nutzung dieser APIs. Sie müssen lediglich einen Konstruktor aufrufen, der in einer der reflektierten Schichtenklassen deklariert ist, und dann den resultierenden Arbeitsspeicher-Allocation
an das RenderScript binden.
In einigen Situationen empfiehlt es sich, diese Klassen direkt zu verwenden, um Speicher selbst zuzuweisen, z. B. wenn Sie eine Bitmap aus einer Ressource laden oder Arbeitsspeicher für Zeiger auf primitive Typen zuweisen möchten. Weitere Informationen hierzu finden Sie im Abschnitt Arbeitsspeicher an RenderScript zuweisen und an RenderScript binden.
In der folgenden Tabelle werden die drei Klassen zur Arbeitsspeicherverwaltung ausführlicher beschrieben:
Android-Objekttyp | Beschreibung |
---|---|
Element |
Ein Element beschreibt eine Zelle einer Speicherzuweisung und kann zwei Formen haben: einfach oder komplex. Ein Basiselement enthält eine einzelne Datenkomponente eines beliebigen gültigen RenderScript-Datentyps.
Beispiele für grundlegende Elementdatentypen sind ein einzelner Komplexe Elemente enthalten eine Liste von Grundelementen und werden aus |
Type |
Ein Typ ist eine Vorlage für die Arbeitsspeicherzuweisung und besteht aus einem Element und einer oder mehreren Dimensionen. Sie beschreibt das Layout des Arbeitsspeichers (im Grunde ein Array aus Ein Typ besteht aus fünf Dimensionen: X, Y, Z, LOD (Detailebene) und Flächen (einer Kubuskarte). Sie können die Dimensionen X, Y und Z innerhalb der Einschränkungen des verfügbaren Arbeitsspeichers auf einen beliebigen positiven ganzzahligen Wert festlegen. Eine Zuweisung mit einer einzelnen Dimension hat eine X-Dimension größer als null, während die Dimensionen Y und Z Null sind, um anzuzeigen, dass sie nicht vorhanden ist. Eine Zuordnung von x=10, y=1 wird z. B. als zweidimensional und x=10 und y=0 als eindimensional betrachtet. Die Dimensionen „LOD“ und „Flächen“ sind boolesche Werte, um anzuzeigen, ob es vorhanden oder nicht vorhanden ist. |
Allocation |
Eine Zuweisung stellt den Arbeitsspeicher für Anwendungen anhand einer Beschreibung des Arbeitsspeichers bereit, die durch eine Es gibt zwei Möglichkeiten, Zuordnungsdaten hochzuladen: Typ aktiviert oder Typ deaktiviert.
Für einfache Arrays gibt es |
Mit Memory arbeiten
Nicht-statische, globale Variablen, die Sie in Ihrem RenderScript deklarieren, werden bei der Kompilierung zugewiesen.
Sie können mit diesen Variablen direkt im RenderScript-Code arbeiten, ohne ihnen auf Android-Framework-Ebene Arbeitsspeicher zuweisen zu müssen. Über die bereitgestellten Zugriffsmethoden, die in den Klassen der reflektierten Ebene generiert werden, hat auch die Framework-Ebene von Android Zugriff auf diese Variablen. Wenn diese Variablen auf der RenderScript-Laufzeitebene initialisiert werden, werden diese Werte verwendet, um die entsprechenden Werte in der Android-Framework-Ebene zu initialisieren. Wenn globale Variablen als const gekennzeichnet sind, wird keine set
-Methode generiert. Weitere Informationen
Hinweis:Wenn Sie bestimmte RenderScript-Strukturen verwenden, die Zeiger enthalten, z. B. rs_program_fragment
und rs_allocation
, müssen Sie zuerst ein Objekt der entsprechenden Android-Framework-Klasse abrufen und dann die Methode set
für diese Struktur aufrufen, um den Arbeitsspeicher an die RenderScript-Laufzeit zu binden. Sie können diese Strukturen nicht direkt auf der RenderScript-Laufzeitebene bearbeiten. Diese Einschränkung gilt nicht für benutzerdefinierte Strukturen, die Zeiger enthalten, da sie von vornherein nicht in eine Klasse von reflektierten Ebenen exportiert werden können. Wenn Sie versuchen, eine nicht-statische, globale Struktur zu deklarieren, die einen Zeiger enthält, wird ein Compiler-Fehler generiert.
RenderScript unterstützt auch Zeiger, aber Sie müssen den Speicher in Ihrem Android-Framework-Code explizit zuweisen. Wenn Sie in Ihrer .rs
-Datei einen globalen Zeiger deklarieren, weisen Sie Arbeitsspeicher über die entsprechende Klasse der reflektierten Schicht zu und binden ihn an die native RenderScript-Ebene. Sie können mit diesem Arbeitsspeicher auf der Android-Framework-Ebene und der RenderScript-Ebene interagieren. So haben Sie die Flexibilität, Variablen in der jeweils passenden Ebene zu ändern.
Dynamischen Speicher zuweisen und an RenderScript binden
Zum Zuweisen von dynamischem Arbeitsspeicher müssen Sie den Konstruktor einer Script.FieldBase
-Klasse aufrufen. Das ist die gängigste Methode. Alternativ können Sie ein Allocation
manuell erstellen, was z. B. für Zeiger von primitiven Typen erforderlich ist. Der Einfachheit halber sollten Sie nach Möglichkeit einen Script.FieldBase
-Klassenkonstruktor verwenden.
Nachdem Sie eine Arbeitsspeicherzuweisung erhalten haben, rufen Sie die reflektierte bind
-Methode des Zeigers auf, um den zugewiesenen Arbeitsspeicher an die RenderScript-Laufzeit zu binden.
Im folgenden Beispiel wird Arbeitsspeicher sowohl für einen primitiven Zeiger (intPointer
) als auch für einen Zeiger auf eine Struktur (touchPoints
) zugewiesen. Außerdem wird der Arbeitsspeicher an das RenderScript gebunden:
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); ... }
Lesen und in den Speicher schreiben
Sie können sowohl in der RenderScript-Laufzeit als auch auf der Android-Framework-Ebene in statisch und dynamisch zugewiesenen Speicher lesen und schreiben.
Bei statisch zugewiesenem Arbeitsspeicher gilt eine Einschränkung der unidirektionalen Kommunikation auf RenderScript-Laufzeitebene. Wenn der RenderScript-Code den Wert einer Variablen ändert, wird er aus Effizienzgründen nicht zurück an die Android-Framework-Ebene gesendet. Der letzte Wert, der vom Android-Framework festgelegt wird, wird bei einem Aufruf der Methode get
immer zurückgegeben. Wenn jedoch der Android-Framework-Code eine Variable ändert, kann diese Änderung automatisch an die RenderScript-Laufzeit gesendet oder später synchronisiert werden. Wenn Sie Daten von der RenderScript-Laufzeit an die Android-Framework-Ebene senden müssen, können Sie diese Einschränkung mit der Funktion rsSendToClient()
umgehen.
Wenn Sie mit dynamisch zugewiesenem Arbeitsspeicher arbeiten, werden alle Änderungen auf der RenderScript-Laufzeitebene zurück an die Android-Framework-Ebene weitergegeben, wenn Sie die Arbeitsspeicherzuweisung mit dem zugehörigen Zeiger geändert haben. Wenn Sie ein Objekt auf der Framework-Ebene von Android ändern, wird die Änderung sofort wieder auf die RenderScript-Laufzeitebene übertragen.
Globale Variablen lesen und schreiben
Das Lesen und Schreiben in globale Variablen ist ein unkomplizierter Prozess. Du kannst die Zugriffsmethoden auf der Ebene des Android-Frameworks verwenden oder direkt im RenderScript-Code festlegen. Änderungen, die Sie im RenderScript-Code vornehmen, werden nicht an die Framework-Ebene von Android zurückgegeben. Weitere Informationen finden Sie hier.
Angenommen, die folgende Struktur ist in einer Datei namens rsfile.rs
deklariert:
typedef struct Point { int x; int y; } Point_t; Point_t point;
Sie können der Struktur direkt in rsfile.rs
Werte zuweisen. Diese Werte werden nicht zurück auf die Android-Framework-Ebene weitergegeben:
point.x = 1; point.y = 1;
Sie können der Struktur auf der Android-Framework-Ebene Werte zuweisen. Diese Werte werden asynchron wieder an die RenderScript-Laufzeitebene zurückgegeben:
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);
So können Sie die Werte in Ihrem RenderScript-Code lesen:
rsDebug("Printing out a Point", point.x, point.y);
Mit dem folgenden Code können Sie die Werte in der Android-Framework-Ebene lesen. Beachten Sie, dass dieser Code nur dann einen Wert zurückgibt, wenn einer auf Android-Framework-Ebene festgelegt wurde. Wenn Sie den Wert nur auf der RenderScript-Laufzeitebene festlegen, erhalten Sie eine Nullzeiger-Ausnahme:
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());
Globale Hinweise lesen und schreiben
Wenn der Arbeitsspeicher auf Android-Framework-Ebene zugewiesen und an die RenderScript-Laufzeit gebunden wurde, können Sie Arbeitsspeicher von der Android-Framework-Ebene aus lesen und schreiben, indem Sie die Methoden get
und set
für diesen Zeiger verwenden.
In der RenderScript-Laufzeitebene können Sie wie gewohnt mit Zeigern in den Arbeitsspeicher lesen und schreiben. Die Änderungen werden im Gegensatz zu statisch zugewiesenem Arbeitsspeicher zurück an die Android-Framework-Ebene weitergegeben.
Mit dem folgenden Zeiger auf einen struct
in einer Datei namens rsfile.rs
wird beispielsweise Folgendes angegeben:
typedef struct Point { int x; int y; } Point_t; Point_t *point;
Wenn Sie bereits Arbeitsspeicher auf der Android-Framework-Ebene zugewiesen haben, können Sie wie gewohnt auf Werte in struct
zugreifen. Alle Änderungen, die Sie über die Zeigervariable an der Struktur vornehmen, sind automatisch für die Android-Framework-Ebene verfügbar:
Kotlin
point[index].apply { x = 1 y = 1 }
Java
point[index].x = 1; point[index].y = 1;
Sie können Werte auch auf der Android-Framework-Ebene lesen und in den Zeiger schreiben:
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);
Wenn der Arbeitsspeicher bereits gebunden ist, müssen Sie ihn nicht bei jeder Änderung an einem Wert wieder an die RenderScript-Laufzeit binden.