Zaawansowany skrypt renderowania

Ponieważ aplikacje korzystające z metody RenderScript nadal działają w maszynie wirtualnej z Androidem, masz dostęp do wszystkich interfejsów API platformy, które znasz, ale możesz używać w razie potrzeby metody RenderScript. Aby ułatwić tę interakcję środowiska wykonawczego RenderScript i platformy RenderScript, warstwa pośrednia kodu to także w celu ułatwienia komunikacji i zarządzania pamięcią między dwoma poziomami kodu. W tym dokumencie znajdziesz więcej informacji na ten temat różne warstwy kodu oraz sposób współdzielenia pamięci między maszyną wirtualną z Androidem Środowisko wykonawcze RenderScript.

Warstwa środowiska wykonawczego RenderScript

Kod RenderScript jest skompilowany w kompaktowej i dobrze zdefiniowanej warstwie środowiska wykonawczego. Interfejsy API środowiska wykonawczego RenderScript obsługują przy użyciu zaawansowanych funkcji obliczeniowych, które można przenosić i skalować automatycznie liczby rdzeni dostępnych w procesorze.

Uwaga: standardowe funkcje C w NDK muszą być na procesorach, przez co skrypt RenderScript nie ma dostępu do tych bibliotek, bo kod RenderScript został zaprojektowany do działania na różnych typach procesorów.

Definiujesz kod RenderScript w .rs i .rsh w katalogu src/ projektu Android. Kod jest skompilowany do pośredniego kodu bajtowego przez Kompilator llvm, który działa w ramach kompilacji Androida. Kiedy aplikacja działa na urządzeniu, kod bajtowy jest następnie skompilowany (dokładnie we właściwym czasie) do kodu kompilator llvm zainstalowany na urządzeniu. Kod maszynowy jest zoptymalizowany pod kątem urządzenia i pamięci podręcznej, więc późniejsze użycie aplikacji z włączoną obsługą RenderScriptu ponownie skompilować kod bajtowy.

Najważniejsze funkcje bibliotek środowiska wykonawczego RenderScript to:

  • Funkcje żądania alokacji pamięci
  • Duży zbiór funkcji matematycznych z przeciążonymi wersjami skalarnymi i wektorowymi z wielu popularnych rutyn. Operacje takie jak dodawanie, mnożenie, iloczyn skalarny i iloczyn wektorowy jak również funkcje arytmetyczne i porównawcze.
  • Procedury konwersji podstawowych typów danych i wektorów, rutyn macierzy oraz daty i godziny rutyny
  • Typy i struktury danych obsługujące system RenderScript, takie jak typy wektorowe dwóch, trzech lub czterech wektorów.
  • Funkcje logowania

Więcej informacji o dostępnych funkcjach znajdziesz w dokumentacji interfejsu API środowiska wykonawczego RenderScript.

Odbita warstwa

Odzwierciedlona warstwa to zbiór klas generowanych przez narzędzia do kompilacji na Androida, które umożliwiają dostęp do środowiska wykonawczego RenderScript ze platformy Androida. Ta warstwa zawiera też metody i konstruktory, które umożliwiają przydzielanie i pracę z pamięcią wskaźników zdefiniowanych w kod RenderScript. Poniższa lista opisuje główne takie komponenty, jak:

  • Każdy utworzony przez Ciebie plik .rs zostanie wygenerowany w klasie o nazwie project_root/gen/package/name/ScriptC_renderscript_filename z wpisz ScriptC. Ten plik to wersja .java Twojego pliku danych .rs, który można wywołać z platformy Androida. Te zajęcia zawierają: następujące elementy z pliku .rs:
    • Funkcje niestatyczne
    • Niestatyczne, globalne zmienne RenderScript. akcesor dla każdej zmiennej są generowane metody, dzięki czemu możesz odczytywać zapisz zmienne RenderScript na urządzeniach z Androidem platformy. Jeśli zmienna globalna zostanie zainicjowana w w środowisku wykonawczym RenderScript, wartości te są wykorzystywane do zainicjuj odpowiednie wartości w platformie Androida warstwę. Jeśli zmienne globalne są oznaczone jako const, metoda set nie jest . Zajrzyj tutaj .

    • Wskaźniki globalne
  • Element struct jest odzwierciedlony w własnej klasie o nazwie project_root/gen/package/name/ScriptField_struct_name o długości Script.FieldBase. Ta klasa reprezentuje tablicę funkcji struct, która umożliwia przydzielenie pamięci na 1 lub więcej instancji tej instancji struct

Funkcje

Funkcje są odzwierciedlane w samej klasie skryptu znajdującej się w project_root/gen/package/name/ScriptC_renderscript_filename Dla: na przykład jeśli zdefiniujesz następującą funkcję w kodzie 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;
}

zostanie wygenerowany następujący kod 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);
}

Funkcje nie mogą zwracać wartości, ponieważ system RenderScript został zaprojektowany tak, asynchroniczny. Gdy kod platformy Androida wywołuje skrypt RenderScript, wywołanie jest umieszczane w kolejce, gdy to możliwe. To ograniczenie umożliwia działanie systemu RenderScript bez wartości stałych i zwiększa wydajność. Jeśli funkcje mogą mieć zwracane wartości, wywołanie będzie blokowane do czasu zwrócenia wartości.

Jeśli chcesz, by kod RenderScript wysyłał wartość z powrotem do platformy Androida, użyj parametru rsSendToClient() .

Zmienne

Zmienne obsługiwanych typów są odzwierciedlane w samej klasie skryptu znajdującej się w project_root/gen/package/name/ScriptC_renderscript_filename Zbiór akcesorów jest generowany dla każdej zmiennej. Jeśli na przykład zdefiniujesz tę zmienną w parametrze Twój kod RenderScript:

uint32_t unsignedInteger = 1;

zostanie wygenerowany następujący kod 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;
}
  

Struktura

Jednostki są odzwierciedlane w osobnych klasach, które znajdują się w <project_root>/gen/com/example/renderscript/ScriptField_struct_name Ten klasa reprezentuje tablicę obiektu struct i pozwala przydzielić pamięć dla określoną liczbę struct s. Jeśli na przykład zdefiniujesz taką strukturę:

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

w ScriptField_Point.java zostanie wygenerowany ten kod:

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

Wygenerowany kod jest udostępniany, aby ułatwić Ci przydzielenie pamięci dla żądanych elementów struct przez środowisko wykonawcze RenderScript i do interakcji z modułami struct w pamięci. Każda klasa struct definiuje te metody i konstruktory:

  • Przeciążone konstruktory, które umożliwiają przydzielanie pamięci. Zezwolenia konstruktora ScriptField_struct_name(RenderScript rs, int count) zdefiniować liczbę struktur, dla których chcesz przydzielić pamięć za pomocą count. Konstruktor ScriptField_struct_name(RenderScript rs, int count, int usages) definiuje dodatkowy parametr usages, który pozwala określić miejsce w ramach tego przydziału pamięci. Dostępne są 4 miejsce w pamięci możliwości:
    • USAGE_SCRIPT: przydziela w pamięci skryptu kosmosu. Jest to domyślne miejsce w pamięci, jeśli nie określisz miejsca w pamięci.
    • USAGE_GRAPHICS_TEXTURE: przydziela w w ramach procesora graficznego.
    • USAGE_GRAPHICS_VERTEX: przydziela w wierzchołku ilość miejsca w pamięci GPU.
    • USAGE_GRAPHICS_CONSTANTS: przydziela w stałe miejsce w pamięci GPU wykorzystywanej przez różne obiekty programu.

    Możesz określić wiele obszarów w pamięci, używając operatora bitowego OR. Robię to powiadamia środowisko wykonawcze RenderScript, by uzyskać dostęp do danych w określone miejsca w pamięci. Ten przykład przydziela pamięć do niestandardowego typu danych w przestrzeniach pamięci skryptów i wierzchołków pamięci:

    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);
    
  • Statyczna zagnieżdżona klasa (Item) umożliwia utworzenie instancji struct w formie obiektu. Ta zagnieżdżona klasa jest przydatna, jeśli warto pracować struct w kodzie Androida. Gdy skończysz manipulować obiektem, możesz przekazać obiekt do przydzielonej pamięci, wywołując funkcję set(Item i, int index, boolean copyNow) i ustawiając Item w żądanej pozycji tablicę. Środowisko wykonawcze RenderScript automatycznie ma dostęp do nowo zapisanej pamięci.
  • Metody metody dostępu do pobierania i ustawiania wartości każdego pola w elemencie struct. Każda z tych opcji metody akcesora mają parametr index określający struct w tablicę, w której chcesz odczytywać dane lub w niej zapisywać. Każda metoda ustawiająca ma również Parametr copyNow, który określa, czy należy natychmiast synchronizować tę pamięć do środowiska wykonawczego RenderScript. Aby zsynchronizować wspomnienie, które nie zostało zsynchronizowane, wywołaj copyAll()
  • Metoda createElement() tworzy opis struktury w pamięci. Ten Opis służy do przydzielania pamięci składającej się z jednego lub wielu elementów.
  • resize() działa prawie jak realloc() w C, pozwalając rozszerzać wcześniej przydzieloną pamięć przy zachowaniu obecnych wartości Utworzono.
  • copyAll() synchronizuje pamięć ustawioną na poziomie platformy z Środowisko wykonawcze RenderScript. Gdy wywołujesz metodę ustawiania akcesora dla użytkownika, pojawia się copyNow parametr logiczny, który możesz określić. Określanie true synchronizuje pamięć, gdy wywołujesz tę metodę. Jeśli wybierzesz wartość Fałsz, możesz wywołać funkcję copyAll() raz, by zsynchronizować pamięć dla wszystkich właściwości, które nie są jeszcze zsynchronizowane.

Wskaźniki

Wskaźniki globalne są odzwierciedlane w samej klasie skryptu znajdującej się w project_root/gen/package/name/ScriptC_renderscript_filename Ty może zadeklarować wskaźniki do struct lub dowolnego z obsługiwanych typów RenderScriptu, ale Pole struct nie może zawierać wskaźników ani zagnieżdżonych tablic. Jeśli na przykład zdefiniujesz parametr następujące wskaźniki do struct i int32_t

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

Point_t *touchPoints;
int32_t *intPointer;

zostanie wygenerowany następujący kod 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;
}
  

Metoda get i specjalna o nazwie bind_pointer_name (zamiast metody set()). Metoda bind_pointer_name umożliwia powiązanie pamięci która jest przydzielona w maszynie wirtualnej z Androidem do środowiska wykonawczego RenderScript (nie możesz przydzielić pamięci w pliku .rs). Więcej informacji znajdziesz w sekcji Praca dzięki przydzielonej pamięci.

Interfejsy API alokacji pamięci

Aplikacje korzystające z metody RenderScript nadal będą działać w maszynie wirtualnej z Androidem. Rzeczywisty kod RenderScript działa natomiast natywnie, potrzebuje dostępu do pamięci przydzielonej w maszynie wirtualnej z Androidem. W tym celu musisz dołącza pamięć przydzieloną w maszynie wirtualnej do środowiska wykonawczego RenderScript. Ten umożliwia środowisko wykonawcze RenderScript bezproblemowo współpracuje z pamięcią, żądań, ale nie można ich jawnie przydzielać. Efekt końcowy jest taki sam, jak w przypadku pod tytułem malloc w języku C. Dodatkową zaletą jest to, że maszyna wirtualna z Androidem może uruchamiać czyszczenie pamięci, a także współdzielą pamięć z warstwą środowiska wykonawczego RenderScript. Powiązanie jest konieczne tylko w przypadku pamięci alokowanej dynamicznie. Statycznie przydzielona pamięć dla kodu RenderScript jest tworzona automatycznie podczas kompilacji. Zobacz Rysunek 1. .

Do obsługi tego systemu przydzielania pamięci istnieje zestaw interfejsów API, które umożliwiają maszynie wirtualnej z Androidem przydzielać pamięć i udostępniać funkcje podobne do wywołania malloc. Te zajęcia zasadniczo opisują sposób przydzielania pamięci i przeprowadzanie tego procesu. Na lepsze zrozumieć, jak działają te zajęcia. Warto myśleć o nich w odniesieniu do prostego malloc może wyglądać tak:

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

Wywołanie malloc można podzielić na 2 części: rozmiar przydzielonej pamięci (sizeof(int)), oraz liczbę jednostek tej pamięci, które powinny zostać przydzielone (10). Platforma Androida udostępnia klasyfikacje dla tych 2 części: oraz klasę reprezentującą samą malloc.

Klasa Element reprezentuje część (sizeof(int)) wywołania malloc i zakrywa 1 komórkę przydziału pamięci, np. wartość zmiennoprzecinkową lub typ elementu. Klasa Type zawiera obiekt Element. oraz liczbę elementów do przydzielenia (w tym przykładzie 10). Możesz określić Type jako tablica Element s. Klasa Allocation wykonuje funkcję alokacja pamięci na podstawie danej wartości Type i reprezentuje rzeczywistą przydzieloną pamięć.

W większości przypadków nie trzeba bezpośrednio wywoływać tych interfejsów API alokacji pamięci. Odbita warstwa klasy generują kod umożliwiający automatyczne korzystanie z tych interfejsów API, a aby przydzielić pamięć, wystarczy wywołać konstruktora zadeklarowanego w jednej z odbitych klas warstwy, a następnie powiązać wynikową pamięć Allocation do RenderScriptu. W niektórych sytuacjach warto używać tych zajęć bezpośrednio, aby przydzielać pamięć własne, np. wczytywanie bitmapy z zasobu lub gdy chcesz przydzielić pamięć na potrzeby wskaźników podstawowych typów. Aby to zrobić, zobacz Przypisywanie i wiązanie pamięci do sekcji RenderScript. W tej tabeli bardziej szczegółowo opisano 3 klasy zarządzania pamięcią:

Typ obiektu na Androidzie Opis
Element

Element opisuje jedną komórkę alokacji pamięci i może mieć 2 postacie: podstawowy lub złożone.

Element podstawowy zawiera pojedynczy komponent danych dowolnego prawidłowego typu danych RenderScript. Przykładami podstawowych typów danych elementów są pojedyncza wartość float, wektor float4 lub jednolity kolor RGB-565.

Elementy złożone zawierają listę podstawowych elementów i są tworzone na podstawie struct zadeklarowane w kodzie RenderScript. Na przykład przydział może zawierać wiele elementów struct rozmieszczonych w kolejności w pamięci. Każdy element struct jest uznawany za własnego elementu, a nie poszczególnych typów danych w tej strukturze.

Type

Typ to szablon alokacji pamięci, który składa się z elementu oraz co najmniej jednego elementu wymiarów. Opisuje układ pamięci (zasadniczo tablica Element), ale nie przydziela pamięci na dane, opisanej przez nas.

Typ składa się z pięciu wymiarów: X, Y, Z, LOD (poziom szczegółowości) i ścianek (sześcianu). mapy). Wymiary X,Y,Z możesz ustawić na dowolną dodatnią liczbę całkowitą w parze klucz-wartość z ograniczeniami dostępnej pamięci. Przydział według jednego wymiaru ma wymiar X równy jest większa od 0, a wymiary Y i Z mają wartość 0, co wskazuje, że nie występuje. Dla: Na przykład przydział x=10, y=1 jest uważany za dwuwymiarowy, a x=10, y=0 to uznawane za jednowymiarowe. Wymiary LOD i Faces są wartościami logicznymi, które wskazują obecność lub brak obecności.

Allocation

Przydział udostępnia pamięć dla aplikacji na podstawie opisu pamięci reprezentowany przez Type. Przydzielona pamięć może istnieć w wiele przestrzeni pamięci jednocześnie. Jeśli pamięć zostanie zmodyfikowana w jednym pokoju, musisz jawnie synchronizować pamięć, aby była ona aktualizowana we wszystkich pozostałych pokojach, w których istnieje.

Dane alokacji są przesyłane na jeden z dwóch głównych sposobów: typ jest zaznaczony, a typ niezaznaczony. W przypadku prostych tablic dostępne są funkcje copyFrom(), które pobierają tablicę z argumentu systemu Android i skopiować go do magazynu pamięci warstwy natywnej. Odznaczone warianty pozwalają system Android do kopiowania tablic struktur, ponieważ nie obsługuje w różnych strukturach. Na przykład jeśli istnieje przydział będący tablicą z n liczb zmiennoprzecinkowych, dane zawarte w tablicy float[n] lub tablicy byte[n*4] można skopiować.

Praca z pamięcią

Niestatyczne, globalne zmienne zadeklarowane w kodzie RenderScript to przydzielona pamięć podczas kompilacji. Możesz z nich korzystać bezpośrednio w kodzie RenderScript bez konieczności przydzielania pamięci na poziomie platformy Androida. Warstwa platformy Androida również ma dostęp do tych zmiennych. z podanymi metodami akcesoriów, które są generowane w odbitych klasach warstwy. Jeśli te zmienne są zainicjowanych w warstwie środowiska wykonawczego RenderScript, wartości te służą do inicjowania w warstwie platformy Androida. Jeśli zmienne globalne są oznaczone jako stałe, metoda set jest nie wygenerowano. Więcej informacji znajdziesz tutaj.

Uwaga: jeśli używasz określonych struktur RenderScript zawierających wskaźniki, na przykład rs_program_fragment i rs_allocation, musisz uzyskać obiekt najpierw odpowiednią klasę platformy Androida, a następnie wywołaj dla niej metodę set powiązać pamięć ze środowiskiem wykonawczym RenderScript. Nie możesz bezpośrednio manipulować tymi obiektami w warstwie środowiska wykonawczego RenderScript. To ograniczenie nie dotyczy struktur zdefiniowanych przez użytkownika które zawierają wskaźniki, ponieważ nie można ich wyeksportować do odzwierciedlonej klasy warstwy po pierwsze. Jeśli spróbujesz zadeklarować niestatyczny, globalny błąd kompilatora struct zawierającego wskaźnik.

RenderScript obsługuje też wskaźniki, ale musisz jawnie przydzielić pamięć w parametrze Kod platformy Androida. Gdy zadeklarujesz w pliku .rs wskaźnik globalny, Przydziel pamięć za pomocą odpowiedniej odbitej klasy warstwy i powiąż ją z natywną Warstwa RenderScript. Możesz korzystać z tej pamięci w warstwie platformy Androida, a także warstwa RenderScript, która umożliwia modyfikowanie zmiennych odpowiednią warstwę.

Przydzielanie i wiązanie pamięci dynamicznej do skryptu RenderScript

Aby przydzielić pamięć dynamiczną, musisz wywołać konstruktora Script.FieldBase, czyli najpopularniejszy sposób. Alternatywnym rozwiązaniem jest utworzenie Allocation ręcznie, co jest wymagane w przypadku takich elementów jak wskaźniki typu podstawowego. Zalecenia dla uproszczenia używaj konstruktora klas Script.FieldBase. Po uzyskaniu przydziału pamięci wywołaj odzwierciedloną metodę bind wskaźnika, aby powiązać przydzieloną pamięć z Środowisko wykonawcze RenderScript.

Poniższy przykład przydziela pamięć zarówno dla wskaźnika typu podstawowego, intPointer i wskaźnik do struktury (struct) touchPoints. Wiąże również pamięć z Skrypt renderowania:

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

   ...
}

Czytanie i zapisywanie w pamięci

Możesz odczytywać oraz zapisywać statycznie i dynamicznie alokowaną pamięć zarówno w środowisku wykonawczym RenderScript, jak i w pamięci alokowanej dynamicznie. i warstwy platformy Androida.

Statycznie przydzielona pamięć podlega ograniczeniom komunikacji jednokierunkowej na poziomie środowiska wykonawczego RenderScript. Gdy kod RenderScript zmienia wartość zmiennej, nie zmienia się ona przekazywane z powrotem do warstwy platformy Androida w celu zwiększenia wydajności. Ostatnia wartość ustawiona na platformie Android jest zawsze zwracana podczas wywołania funkcji get. . Jeśli jednak kod platformy Androida modyfikuje zmienną, można o tym poinformować w środowisku wykonawczym RenderScript lub na później. Jeśli chcesz wysłać dane od środowiska wykonawczego RenderScript po warstwę platformy Androida, możesz użyć funkcji Funkcja rsSendToClient() do przezwyciężenia.

Podczas pracy z dynamicznie alokowaną pamięcią wszelkie zmiany w warstwie środowiska wykonawczego RenderScript są rozpowszechniane z powrotem do warstwy platformy Androida, jeśli przydział pamięci został zmodyfikowany za pomocą powiązanego z nią wskaźnika. Zmodyfikowanie obiektu w warstwie platformy Androida powoduje natychmiastowe propagowanie tej zmiany z powrotem do skryptu RenderScript warstwa środowiska wykonawczego.

Odczyt i zapis zmiennych globalnych

Odczyt i zapis zmiennych globalnych to prosty proces. Możesz używać metod akcesora na poziomie platformy Androida lub ustawić je bezpośrednio w kodzie RenderScript. Pamiętaj, że wszystkie zmiany wprowadzone w kodzie RenderScript nie są rozpowszechniane powrót do warstwy platformy Androida (kliknij tutaj) ).

Na przykład przy tej strukturze zadeklarowanej w pliku o nazwie rsfile.rs:

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

Point_t point;

Możesz przypisać wartości do tego typu struktury bezpośrednio w rsfile.rs. Te wartości nie są i rozpowszechniane z powrotem na poziom platformy Androida:

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

Możesz w ten sposób przypisać wartości do struktury w warstwie platformy Androida. Wartości te są przekazywane z powrotem do środowiska wykonawczego RenderScript asynchronicznie:

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

Wartości w kodzie RenderScript możesz odczytać w ten sposób:

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

Wartości możesz odczytać w warstwie platformy Androida za pomocą poniższego kodu. Pamiętaj, że ten krok kod zwraca wartość tylko wtedy, gdy została ona ustawiona na poziomie platformy Androida. Otrzymasz wskaźnik o wartości null wyjątkiem, jeśli ustawisz wartość tylko na poziomie środowiska wykonawczego 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());

Odczytywanie i zapisywanie wskaźników globalnych

Zakładając, że pamięć została przydzielona na poziomie platformy Androida i powiązana ze środowiskiem wykonawczym RenderScript, możesz odczytywać i zapisywać pamięć na poziomie platformy Androida przy użyciu metod get i set dla tego wskaźnika. W warstwie środowiska wykonawczego RenderScript możesz w zwykły sposób odczytywać i zapisywać w pamięci za pomocą wskaźników, a zmiany są rozpowszechniane. do warstwy platformy Androida – w przeciwieństwie do pamięci przydzielonej statycznie.

Na przykład ten wskaźnik do pola struct w pliku o nazwie rsfile.rs:

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

Point_t *point;

Zakładając, że pamięć została już przydzielona w warstwie platformy Androida, możesz uzyskać dostęp do wartości w struct. Wszelkie zmiany wprowadzone w strukturze za pomocą jej zmiennej wskaźnika są automatycznie dostępne w warstwie platformy Androida:

Kotlin

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

Java

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

Wartości wskaźnika możesz odczytywać i zapisywać również w warstwie platformy Androida:

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

Gdy pamięć będzie już powiązana, nie musisz ponownie tworzyć jej dla kodu RenderScript w czasie działania po każdej zmianie wartości.