RenderScript Lanjutan

Aplikasi yang menggunakan RenderScript masih berjalan di dalam VM Android sehingga Anda memiliki akses ke semua API framework yang sudah biasa Anda gunakan, tetapi juga dapat memanfaatkan RenderScript jika perlu. Untuk memudahkan interaksi antara framework dan runtime RenderScript ini, lapisan kode menengah juga tersedia untuk mempermudah manajemen memori dan komunikasi di antara kedua level kode. Dokumen ini membahas berbagai lapisan kode ini secara lebih mendetail, serta bagaimana memori dibagi antara VM Android dan runtime RenderScript.

Lapisan Runtime RenderScript

Kode RenderScript Anda dikompilasi dan dieksekusi dalam lapisan runtime yang ringkas dan didefinisikan dengan baik. API runtime RenderScript menawarkan dukungan untuk komputasi intensif yang portabel dan skalabel secara otomatis sesuai jumlah core yang tersedia pada prosesor.

Catatan: Fungsi C standar dalam NDK harus dipastikan berjalan pada salah satu CPU, sehingga RenderScript tidak dapat mengakses library ini, karena RenderScript dirancang untuk berjalan pada berbagai jenis prosesor.

Tentukan kode RenderScript dalam file .rs dan .rsh di direktori src/ project Android Anda. Kode akan dikompilasi menjadi bytecode menengah oleh compiler llvm yang berjalan sebagai bagian dari build Android. Saat aplikasi Anda berjalan di perangkat, bytecode kemudian akan dikompilasi (pada saat yang sama) ke kode mesin oleh kompilator llvm lain yang berada di perangkat tersebut. Kode mesin dioptimalkan untuk perangkat dan juga di-cache sehingga penggunaan aplikasi yang didukung RenderScript berikutnya tidak mengompilasi ulang bytecode tersebut.

Beberapa fitur utama library runtime RenderScript meliputi:

  • Fitur permintaan alokasi memori
  • Sekumpulan besar fungsi matematika dengan versi kelebihan muatan berjenis skalar dan vektor dari berbagai rutinitas umum. Tersedia operasi seperti penambahan, perkalian, dot product, dan cross product, serta fungsi perbandingan dan aritmetika simpel.
  • Rutinitas konversi untuk vektor dan jenis data primitif, rutinitas matriks, serta rutinitas tanggal dan waktu
  • Struktur dan jenis data untuk mendukung sistem RenderScript, seperti jenis Vektor untuk menentukan dua, tiga, atau empat vektor.
  • Fungsi logging

Lihat referensi API runtime RenderScript untuk informasi selengkapnya tentang fungsi yang tersedia.

Lapisan yang Direfleksikan

Lapisan yang direfleksikan adalah sekumpulan class yang dihasilkan oleh fitur build Android untuk memungkinkan akses ke runtime RenderScript dari framework Android. Lapisan ini juga menyediakan metode dan konstruktor yang memungkinkan Anda mengalokasikan dan bekerja dengan memori untuk pointer yang ditentukan dalam kode RenderScript Anda. Daftar berikut menjelaskan komponen utama yang direfleksikan:

  • Setiap file .rs yang Anda buat akan diubah menjadi class yang bernama project_root/gen/package/name/ScriptC_renderscript_filename dengan jenis ScriptC. File ini adalah versi .java file .rs Anda, yang dapat dipanggil dari framework Android. Class ini berisi item berikut yang direfleksikan dari file .rs:
    • Fungsi nonstatis
    • Variabel RenderScript global nonstatis. Metode pengakses akan dibuat untuk setiap variabel sehingga Anda dapat membaca dan menulis variabel RenderScript dari framework Android. Jika variabel global diinisialisasi pada lapisan runtime RenderScript, nilai tersebut akan digunakan untuk menginisialisasi nilai yang berkaitan dalam lapisan framework Android. Jika variabel global ditandai sebagai const, metode set tidak akan dibuat. Lihat di sini untuk detail selengkapnya.

    • Pointer global
  • struct direfleksikan ke dalam class-nya sendiri yang bernama project_root/gen/package/name/ScriptField_struct_name, yang memperluas Script.FieldBase. Class ini merepresentasikan array struct, yang memungkinkan Anda mengalokasikan memori untuk satu atau beberapa instance dari struct ini.

Fungsi

Fungsi direfleksikan ke dalam class skrip sendiri, yang berada di project_root/gen/package/name/ScriptC_renderscript_filename. Sebagai contoh, jika Anda menentukan fungsi berikut dalam kode RenderScript Anda:

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

kode Java berikut akan dibuat:

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

Fungsi tidak boleh memiliki nilai hasil, karena sistem RenderScript didesain agar berjalan secara asinkron. Saat kode framework Android memanggil RenderScript, panggilan tersebut akan diantrekan dan dijalankan ketika memungkinkan. Batasan ini memungkinkan sistem RenderScript berfungsi tanpa gangguan konstan, yang kemudian akan meningkatkan efisiensi. Jika fungsi diperbolehkan memiliki nilai hasil, panggilan akan diblokir hingga nilai tersebut dihasilkan.

Jika Anda ingin kode RenderScript mengirimkan nilai kembali ke framework Android, gunakan fungsi rsSendToClient().

Variabel

Variabel dari jenis yang didukung direfleksikan ke dalam class skrip sendiri, yang berada di project_root/gen/package/name/ScriptC_renderscript_filename. Serangkaian metode pengakses akan dibuat untuk setiap variabel. Sebagai contoh, jika Anda menentukan variabel berikut dalam kode RenderScript Anda:

uint32_t unsignedInteger = 1;

kode Java berikut akan dibuat:

private long mExportVar_unsignedInteger;
public void set_unsignedInteger(long v){
    mExportVar_unsignedInteger = v;
    setVar(mExportVarIdx_unsignedInteger, v);
}

public long get_unsignedInteger(){
    return mExportVar_unsignedInteger;
}
  

Struct

Struct direfleksikan ke dalam class-nya sendiri, yang berada di <project_root>/gen/com/example/renderscript/ScriptField_struct_name. Class ini merepresentasikan array dari struct dan memungkinkan Anda mengalokasikan memori untuk struct dalam jumlah tertentu. Sebagai contoh, jika Anda menentukan struct berikut:

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

kode berikut akan dibuat di 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 */);
    }
}

Kode yang dihasilkan kemudian diberikan kepada Anda untuk memudahkan alokasi memori bagi struct yang diminta oleh runtime RenderScript dan untuk berinteraksi dengan struct dalam memori. Setiap class struct menentukan metode dan konstruktor berikut:

  • Konstruktor kelebihan muatan yang memungkinkan Anda mengalokasikan memori. Konstruktor ScriptField_struct_name(RenderScript rs, int count) memungkinkan Anda menentukan jumlah struktur yang diinginkan untuk mengalokasikan memori dengan parameter count. Konstruktor ScriptField_struct_name(RenderScript rs, int count, int usages) menentukan parameter tambahan, usages, yang memungkinkan Anda menentukan ruang memori alokasi memori ini. Terdapat empat kemungkinan ruang memori:

    Anda dapat menentukan beberapa ruang memori menggunakan operator OR bitwise. Dengan demikian, runtime RenderScript akan diberi tahu bahwa Anda ingin mengakses data di ruang memori yang ditentukan. Contoh berikut mengalokasikan memori untuk jenis data kustom di ruang memori skrip dan verteks:

    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);
    
  • Class bertingkat yang bersifat statis, Item, memungkinkan Anda membuat instance struct dalam bentuk objek. Class bertingkat ini berguna jika dirasa lebih logis saat bekerja dengan struct dalam kode Android Anda. Setelah selesai memanipulasi objek tersebut, Anda dapat memasukkannya ke memori yang dialokasikan dengan memanggil set(Item i, int index, boolean copyNow) dan mengatur Item ke posisi yang diinginkan dalam array. Runtime RenderScript akan otomatis memiliki akses ke memori yang baru ditulis tersebut.
  • Metode pengakses untuk mendapatkan dan menentukan nilai setiap kolom dalam struct. Masing-masing metode pengakses ini memiliki parameter index untuk menetapkan struct dalam array tujuan pembacaan atau penulisan Anda. Setiap metode penyetel juga memiliki parameter copyNow yang menetapkan apakah memori ini akan langsung disinkronkan ke runtime RenderScript atau tidak. Untuk menyinkronkan memori yang belum disinkronkan, panggil copyAll().
  • Metode createElement() akan membuat deskripsi struct dalam memori. Deskripsi ini digunakan untuk mengalokasikan memori yang terdiri dari satu atau beberapa elemen.
  • resize() bekerja seperti realloc() di C, yang memungkinkan Anda memperluas memori yang telah dialokasikan, dan mempertahankan nilai saat ini yang dibuat sebelumnya.
  • copyAll() menyinkronkan memori yang telah ditentukan pada level framework ke runtime RenderScript. Saat Anda memanggil metode pengakses yang telah ditentukan pada sebuah anggota, terdapat parameter boolean copyNow opsional yang dapat Anda tetapkan. Menetapkan true akan menyinkronkan memori saat Anda memanggil metode tersebut. Jika menetapkan false, Anda dapat memanggil copyAll() sekali, dan tindakan ini akan menyinkronkan memori untuk semua properti yang belum disinkronkan.

Pointer

Pointer global direfleksikan ke dalam class skrip sendiri, yang berada di project_root/gen/package/name/ScriptC_renderscript_filename. Anda dapat mendeklarasikan pointer ke struct atau salah satu jenis RenderScript yang didukung, tetapi struct tidak boleh berisi pointer atau array bertingkat. Sebagai contoh, jika Anda menentukan pointer berikut ke struct dan int32_t

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

Point_t *touchPoints;
int32_t *intPointer;

kode Java berikut akan dibuat:

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

Metode get dan metode khusus bernama bind_pointer_name (bukan metode set()) akan dibuat. Metode bind_pointer_name memungkinkan Anda mengikat memori yang dialokasikan di VM Android ke runtime RenderScript (Anda tidak dapat mengalokasikan memori dalam file .rs). Untuk informasi selengkapnya, lihat Bekerja dengan Memori yang Dialokasikan.

API Alokasi Memori

Aplikasi yang menggunakan RenderScript masih berjalan di VM Android. Namun, kode RenderScript yang sebenarnya berjalan secara native dan memerlukan akses ke memori yang dialokasikan di VM Android. Untuk melakukannya, Anda harus menyematkan memori yang dialokasikan dalam VM ke runtime RenderScript. Proses yang disebut binding ini memungkinkan runtime RenderScript bekerja tanpa masalah dengan memori yang dimintanya tetapi tidak dapat dialokasikan secara eksplisit. Hasil akhirnya pada dasarnya sama seperti jika Anda memanggil malloc di C. Keuntungan lainnya adalah bahwa VM Android dapat melakukan pembersihan sampah memori serta berbagi memori dengan lapisan runtime RenderScript. Binding hanya diperlukan untuk memori yang dialokasikan secara dinamis. Memori yang dialokasikan secara statis otomatis dibuat untuk kode RenderScript pada waktu kompilasi. Lihat Gambar 1 untuk informasi selengkapnya tentang cara alokasi memori dilakukan.

Untuk mendukung sistem alokasi memori ini, ada sekumpulan API yang memungkinkan VM Android mengalokasikan memori dan menawarkan fungsionalitas serupa ke panggilan malloc. Class ini pada dasarnya menjelaskan bagaimana memori harus dialokasikan, kemudian juga melakukan alokasi tersebut. Untuk lebih memahami cara kerjanya, coba pikirkan class tersebut sehubungan dengan panggilan malloc sederhana yang mungkin terlihat seperti berikut:

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

Panggilan malloc dapat dipecah menjadi dua bagian: ukuran memori yang dialokasikan (sizeof(int)), beserta berapa banyak unit dari memori tersebut yang harus dialokasikan (10). Framework Android menyediakan class untuk dua bagian ini serta class untuk merepresentasikan malloc sendiri.

Class Element merepresentasikan bagian (sizeof(int)) dari panggilan malloc dan mengenkapsulasi satu sel alokasi memori, seperti nilai float tunggal atau struct. Class Type mengenkapsulasi Element dan jumlah elemen yang akan dialokasikan (10 dalam contoh kami). Anda dapat menganggap Type sebagai array Element. Class Allocation melakukan alokasi memori aktual berdasarkan Type yang ditentukan dan merepresentasikan memori aktual yang dialokasikan.

Biasanya, Anda tidak perlu memanggil API alokasi memori ini secara langsung. Class lapisan yang direfleksikan akan menghasilkan kode untuk otomatis menggunakan API ini, dan untuk mengalokasikan memori, Anda hanya perlu memanggil konstruktor yang dideklarasikan dalam salah satu class lapisan yang direfleksikan, kemudian mengikat Allocation memori yang dihasilkan ke RenderScript. Terkadang, Anda mungkin ingin menggunakan class ini secara langsung untuk mengalokasikan memori sendiri, seperti memuat bitmap dari resource atau saat Anda ingin mengalokasikan memori untuk pointer ke jenis primitif. Untuk mencari tahu cara melakukannya, lihat bagian Mengalokasikan dan mengikat memori ke RenderScript. Tabel berikut menjelaskan tiga class manajemen memori secara lebih mendetail:

Jenis Objek Android Deskripsi
Element

Elemen menjelaskan satu sel alokasi memori dan dapat memiliki dua bentuk: dasar atau kompleks.

Elemen dasar berisi satu komponen data dari setiap jenis data RenderScript yang valid. Contoh jenis data elemen dasar mencakup satu nilai float, vektor float4, atau satu warna RGB-565.

Elemen kompleks berisi sejumlah elemen dasar dan dibuat dari struct yang Anda deklarasikan dalam kode RenderScript. Contohnya, satu alokasi dapat berisi beberapa struct yang disusun berurutan dalam memori. Setiap struct dianggap sebagai elemennya sendiri, bukan setiap jenis data dalam struct tersebut.

Type

Jenis adalah template alokasi memori dan terdiri dari satu elemen serta satu atau beberapa dimensi. Jenis menjelaskan tata letak memori (pada dasarnya array Element), tetapi tidak mengalokasikan memori untuk data yang dijelaskannya.

Jenis terdiri dari lima dimensi: X, Y, Z, LOD (level detail), dan Faces (dari peta kubus). Anda dapat menentukan dimensi X,Y,Z ke nilai bilangan bulat positif apa pun dalam batasan memori yang tersedia. Alokasi dimensi tunggal memiliki dimensi X yang lebih besar dari nol, sementara dimensi Y dan Z miliknya adalah nol untuk mengindikasikan ketidakadaan. Sebagai contoh, alokasi x=10, y=1 dianggap dua dimensi, sedangkan x=10, y=0 dianggap satu dimensi. Dimensi LOD dan Faces berupa boolean untuk menunjukkan ada atau tidak ada.

Allocation

Alokasi menyediakan memori untuk aplikasi berdasarkan deskripsi memori yang direpresentasikan oleh Type. Memori yang dialokasikan dapat berada di banyak ruang memori secara bersamaan. Jika memori diubah di satu ruang, Anda harus menyinkronkan memori secara eksplisit agar diupdate di semua ruang lain tempatnya berada.

Data alokasi diupload melalui salah satu dari dua cara utama: dengan pemeriksaan jenis dan tanpa pemeriksaan jenis. Untuk array sederhana, terdapat fungsi copyFrom() yang menggunakan array dari sistem Android dan menyalinnya ke ruang memori lapisan native. Varian yang tidak diperiksa memungkinkan sistem Android menyalin seluruh array struktur karena sistem ini tidak mendukung struktur. Sebagai contoh, jika ada alokasi yang merupakan array n float, data yang terdapat dalam array float[n] atau array byte[n*4] dapat disalin.

Bekerja dengan Memori

Variabel global nonstatis yang Anda deklarasikan dalam RenderScript akan diberi alokasi memori pada waktu kompilasi. Anda dapat bekerja dengan variabel ini secara langsung dalam kode RenderScript tanpa harus mengalokasikan memori untuknya pada level framework Android. Lapisan framework Android juga memiliki akses ke variabel ini, dengan metode pengakses yang disediakan dan dibuat dalam class lapisan yang direfleksikan. Jika variabel ini diinisialisasi pada lapisan runtime RenderScript, nilai tersebut akan digunakan untuk menginisialisasi nilai yang terkait dalam lapisan framework Android. Jika variabel global ditandai sebagai konstanta, metode set tidak akan dibuat. Lihat di sini untuk detail selengkapnya.

Catatan: Jika menggunakan struktur RenderScript tertentu yang berisi pointer, seperti rs_program_fragment dan rs_allocation, Anda harus mendapatkan objek class framework Android yang terkait terlebih dahulu, lalu memanggil metode set untuk struktur tersebut agar mengikat memori ke runtime RenderScript. Anda tidak dapat langsung memanipulasi struktur ini pada lapisan runtime RenderScript. Batasan ini tidak berlaku untuk struktur yang ditetapkan pengguna yang berisi pointer, karena sejak awal struktur tersebut tidak dapat diekspor ke class lapisan yang direfleksikan. Error compiler akan muncul jika Anda mencoba mendeklarasikan struct global nonstatis yang berisi pointer.

RenderScript juga memiliki dukungan untuk pointer, tetapi Anda harus mengalokasikan memori secara eksplisit dalam kode framework Android Anda. Saat mendeklarasikan pointer global dalam file .rs, alokasikan memori melalui class lapisan yang direfleksikan yang sesuai dan ikat memori tersebut ke lapisan RenderScript native. Anda dapat berinteraksi dengan memori ini dari lapisan framework Android serta lapisan RenderScript, yang menawarkan fleksibilitas untuk memodifikasi variabel pada lapisan yang paling sesuai.

Mengalokasikan dan mengikat memori dinamis ke RenderScript

Untuk mengalokasikan memori dinamis, Anda harus memanggil konstruktor class Script.FieldBase, yang merupakan cara paling umum. Alternatifnya, Anda dapat membuat file Allocation secara manual, yang diperlukan untuk hal-hal seperti pointer jenis primitif. Anda harus menggunakan konstruktor class Script.FieldBase apabila tersedia agar semuanya tetap ringkas. Setelah mendapatkan alokasi memori, panggil metode bind yang direfleksikan dari pointer untuk mengikat memori yang dialokasikan ke runtime RenderScript.

Contoh di bawah ini mengalokasikan memori untuk pointer jenis primitif, intPointer, dan pointer ke struct, touchPoints. Contoh ini juga mengikat memori ke 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);

   ...
}

Membaca dan menulis ke memori

Anda dapat membaca dan menulis ke memori yang dialokasikan secara statis dan dinamis, baik pada runtime RenderScript maupun lapisan framework Android.

Memori yang dialokasikan secara statis dilengkapi dengan batasan komunikasi satu arah pada level runtime RenderScript. Ketika kode RenderScript mengubah nilai suatu variabel, perubahan ini tidak dikomunikasikan kembali ke lapisan framework Android untuk tujuan efisiensi. Nilai terakhir yang ditentukan dari framework Android selalu dikembalikan selama panggilan ke metode get. Namun, ketika kode framework Android memodifikasi suatu variabel, perubahan tersebut dapat otomatis dikomunikasikan ke runtime RenderScript atau disinkronkan di lain waktu. Jika perlu mengirim data dari runtime RenderScript ke lapisan framework Android, Anda dapat menggunakan fungsi rsSendToClient() untuk mengatasi batasan ini.

Saat bekerja dengan memori yang dialokasikan secara dinamis, setiap perubahan pada lapisan runtime RenderScript akan disebarkan kembali ke lapisan framework Android jika Anda mengubah alokasi memori menggunakan pointer yang terkait. Memodifikasi objek pada lapisan framework Android akan langsung menyebarkan kembali perubahan tersebut ke lapisan runtime RenderScript.

Membaca dan menulis ke variabel global

Membaca dan menulis ke variabel global adalah proses yang mudah. Anda dapat menggunakan metode pengakses pada level framework Android, atau menentukannya secara langsung dalam kode RenderScript. Perlu diingat bahwa setiap perubahan yang Anda lakukan dalam kode RenderScript tidak akan disebarkan kembali ke lapisan framework Android (lihat di sini untuk detail selengkapnya).

Sebagai contoh, dengan struct berikut yang dideklarasikan dalam file bernama rsfile.rs:

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

Point_t point;

Anda dapat menetapkan nilai ke struct seperti berikut secara langsung dalam rsfile.rs. Nilai ini tidak disebarkan kembali ke level framework Android:

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

Anda dapat menetapkan nilai ke struct pada lapisan framework Android seperti berikut. Nilai ini disebarkan kembali ke level runtime RenderScript secara asinkron:

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

Anda dapat membaca nilai tersebut dalam kode RenderScript seperti berikut:

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

Anda dapat membaca nilai dalam lapisan framework Android dengan kode berikut. Perlu diingat bahwa kode ini hanya akan menampilkan nilai jika salah satunya telah ditentukan pada level framework Android. Anda akan mendapatkan pengecualian pointer null jika hanya menentukan nilai pada level runtime 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());

Membaca dan menulis pointer global

Dengan asumsi bahwa memori telah dialokasikan di level framework Android dan diikat ke runtime RenderScript, Anda dapat membaca dan menulis memori dari level framework Android menggunakan metode get dan set untuk pointer tersebut. Pada lapisan runtime RenderScript, Anda dapat membaca dan menulis ke memori dengan pointer seperti biasa, dan perubahannya akan disebarkan kembali ke lapisan framework Android, tidak seperti memori yang dialokasikan secara statis.

Sebagai contoh, dengan pointer ke struct berikut dalam file bernama rsfile.rs:

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

Point_t *point;

Dengan asumsi Anda sudah mengalokasikan memori pada lapisan framework Android, Anda dapat mengakses nilai dalam struct seperti biasa. Setiap perubahan yang Anda lakukan pada struct melalui variabel pointernya akan otomatis tersedia di lapisan framework Android:

Kotlin

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

Java

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

Anda juga dapat membaca dan menulis nilai ke pointer di lapisan 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);

Setelah memori diikat, Anda tidak perlu mengikatnya ulang ke runtime RenderScript setiap kali melakukan perubahan pada nilai.