進階 RenderScript

由於使用 RenderScript 的應用程式仍會在 Android VM 內部執行,您可以存取慣用的所有架構 API,但在適當時機使用 RenderScript。為了促進架構與 RenderScript 執行階段之間的互動,也會顯示中繼程式碼層,以促進兩個程式碼層級之間的通訊和記憶體管理。本文件會進一步詳細介紹這些不同的程式碼層,以及 Android VM 和 RenderScript 執行階段如何共用記憶體。

RenderScript 執行階段層

RenderScript 程式碼會經過編譯,並在定義明確的密集執行階段層中執行。RenderScript 執行階段 API 支援可攜式密集運算,而且會根據處理器上的可用核心數量自動擴充。

注意:務必確保在 CPU 上執行 NDK 的標準 C 函式,如此一來,RenderScript 才無法存取這些程式庫,因為 RenderScript 是專為在不同類型的處理器上執行所設計。

請在 Android 專案的 src/ 目錄中,在 .rs.rsh 檔案中定義 RenderScript 程式碼。程式碼會做為 Android 版本的一部分,由 llvm 編譯器編譯為中繼位元碼。應用程式在裝置上執行時,位元碼將由裝置上的其他 llvm 編譯器編譯 (即時) 為機器碼。機器碼已針對裝置進行最佳化和快取,因此後續使用支援 RenderScript 的應用程式時,不會重新編譯位元碼。

RenderScript 執行階段程式庫的主要功能包括:

  • 記憶體配置要求功能
  • 大量數學函式集合,含有許多常用處理常式的純量和向量型別多載版本。提供加法、乘法、內積和交叉乘積等運算,以及原子算術和比較函式。
  • 原始資料類型和向量的轉換處理常式、矩陣處理常式,以及日期和時間處理常式
  • 支援 RenderScript 系統的資料類型和結構,例如用於定義二、三或四向量的向量類型。
  • 記錄函式

請參閱 RenderScript 執行階段 API 參考資料,進一步瞭解可用的函式。

反映層

反映層是 Android 建構工具產生的一組類別,允許存取 Android 架構中的 RenderScript 執行階段。此層也提供方法和建構函式,方便您針對 RenderScript 程式碼中定義的指標來分配及處理記憶體。下列清單說明反映的主要元件:

  • 您建立的所有 .rs 檔案都會歸入名為 ScriptC 類型的 project_root/gen/package/name/ScriptC_renderscript_filename 類別。這個檔案是 .rs 檔案的 .java 版本,可透過 Android 架構叫出。這個類別包含 .rs 檔案反映出的下列項目:
    • 非靜態函式
    • 非靜態、全域 RenderScript 變數。系統會為每個變數產生存取子方法,因此可透過 Android 架構讀取及寫入 RenderScript 變數。如果在 RenderScript 執行階段層初始化全域變數,這些值將用來初始化 Android 架構層中的對應值。如果將全域變數標示為 const,則系統不會產生 set 方法。詳情請參閱此處

    • 全域指標
  • struct 會反映在名為 project_root/gen/package/name/ScriptField_struct_name 的專屬類別中,該類別會擴充 Script.FieldBase。這個類別代表 struct 的陣列,可讓您為這個 struct 的一或多個執行個體分配記憶體。

函式

函式會反映在指令碼類別本身 (位於 project_root/gen/package/name/ScriptC_renderscript_filename 中)。例如,如果在 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;
}

隨即產生下列 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);
}

由於 RenderScript 系統專為非同步所設計,因此函式無法獲得傳回值。當 Android 架構程式碼呼叫 RenderScript 時,呼叫會排入佇列,並在可行情況下執行。這項限制可讓 RenderScript 系統正常運作,而且不會持續中斷,進而提升效率。如果允許函式獲得傳回值,則呼叫會遭到封鎖,直到回傳數值為止。

若要讓 RenderScript 程式碼將值回傳至 Android 架構,請使用 rsSendToClient() 函式。

變數

支援類型的變數會反映在指令碼類別本身 (位於 project_root/gen/package/name/ScriptC_renderscript_filename 中)。系統會為每個變數產生一組存取子方法。例如,如果在 RenderScript 程式碼中定義下列變數:

uint32_t unsignedInteger = 1;

隨即產生下列 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;
}
  

結構

結構會反映在專屬類別中 (位於 <project_root>/gen/com/example/renderscript/ScriptField_struct_name 中)。這個類別代表 struct 的陣列,可讓您為指定數量的 struct 分配記憶體。例如,如果定義下列結構:

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

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

提供產生的程式碼給您,方便您為 RenderScript 執行階段要求的結構分配記憶體,並與記憶體中的 struct 互動。每個 struct 的類別定義下列方法和建構函式:

  • 可讓您分配記憶體的多載建構函式。ScriptField_struct_name(RenderScript rs, int count) 建構函式可讓您使用 count 參數定義要為其分配記憶體的結構數量。ScriptField_struct_name(RenderScript rs, int count, int usages) 建構函式定義額外的參數 usages,讓您指定這個記憶體配置的記憶體空間。可用的記憶體空間共有四種:

    您可以使用位元 OR 運算子指定多個記憶體空間。此操作會通知 RenderScript 執行階段,表示您希望存取指定記憶體空間中的資料。下列範例會在指令碼和頂點記憶體空間中,為自訂資料類型分配記憶體:

    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);
    
  • 靜態巢狀類別 Item 可讓您以物件的形式建立 struct 的執行個體。如果處理 Android 程式碼中的 struct 顯得較為合理,則適合使用這個巢狀類別。完成物件操控後,您可以呼叫 set(Item i, int index, boolean copyNow) 並將 Item 設為陣列中的所需位置,藉此將物件推送至已分配的記憶體。RenderScript 執行階段會自動獲得新寫入的記憶體的存取權。
  • 取得及設定結構中的每個欄位值的存取子方法。每一個存取子方法都有 index 參數,可指定要讀取或寫入的陣列中的 struct。每個 setter 方法也有一個 copyNow 參數,可指定是否要立即將這個記憶體同步到 RenderScript 執行階段。若要同步處理所有尚未同步的記憶體,請呼叫 copyAll()
  • createElement() 方法會在記憶體中建立結構的說明。此說明專門用來分配由一或多個元素組成的記憶體。
  • resize() 的運作方式與 C 中的 realloc() 類似,可讓您擴充先前分配的記憶體,藉此維持先前建立的目前值。
  • copyAll() 會將在架構層級設定的記憶體,同步到 RenderScript 執行階段。呼叫針對成員設定的存取子方法時,將會提供可供指定的選用 copyNow 布林值參數。指定 true 後,就能在呼叫方法時同步處理記憶體。如果您指定 false,可呼叫 copyAll() 一次,其會同步處理所有尚未同步的屬性的記憶體。

指標

全域指標會反映在指令碼類別本身 (位於 project_root/gen/package/name/ScriptC_renderscript_filename 中)。您可以宣告指向 struct 或任何支援的 RenderScript 類型的指標,但 struct 不得包含指標或巢狀陣列。例如,如果定義下列指向 structint32_t 的指標

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

Point_t *touchPoints;
int32_t *intPointer;

隨即產生下列 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;
}
  

系統會產生 get 方法和名為 bind_pointer_name 的特殊方法 (而不是 set() 方法)。bind_pointer_name 方法可讓您將在 Android VM 中分配的記憶體繫結至 RenderScript 執行階段 (您無法在 .rs 檔案中分配記憶體)。如需詳細資訊,請參閱處理已分配記憶體

記憶體配置 API

使用 RenderScript 的應用程式仍會在 Android VM 中執行。不過實際的 RenderScript 程式碼則能以原生方式執行,並且需要存取在 Android VM 中分配的記憶體。為此,您必須將在 VM 中分配的記憶體附加至 RenderScript 執行階段。這個過程稱為繫結,可讓 RenderScript 執行階段順暢地處理其要求的記憶體,但無法明確分配。最終結果基本上與您在 C 中呼叫 malloc 相同。附加的好處是 Android VM 可執行垃圾收集作業,以及與 RenderScript 執行階段層共用記憶體。只有動態分配的記憶體需要繫結。在編譯期間,系統會為 RenderScript 程式碼自動建立靜態分配的記憶體。若要進一步瞭解記憶體配置的產生方式,請參閱圖 1

為了支援這個記憶體配置系統,有一組 API 可讓 Android VM 分配記憶體,並提供與 malloc 呼叫相似的功能。這些類別主要說明應如何分配記憶體,以及執行配置。為了更加瞭解這些類別的運作方式,建議可參考如下所示的簡單 malloc 呼叫:

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

malloc 呼叫可分成兩個部分:分配的記憶體大小 (sizeof(int)),以及該記憶體應分配的單位數 (10)。Android 架構提供這兩個部分的類別,以及代表 malloc 本身的類別。

Element 類別代表 malloc 呼叫的 (sizeof(int)) 部分,並封裝記憶體配置的一個細胞元,例如單一浮點值或結構。Type 類別會封裝 Element 和要分配的元素數量 (在此範例中為 10)。您可將 Type 視為 Element 的陣列。Allocation 類別會根據指定 Type 執行實際的記憶體配置,並代表實際分配的記憶體。

在大多數情況下,您不需要直接呼叫這些記憶體配置 API。反映層類別會產生程式碼,以自動使用這些 API,而且分配記憶體時,您只需要呼叫在其中一個反映層類別中宣告的建構函式,然後再將產生的記憶體 Allocation 繫結至 RenderScript。在某些情況下,您可能想直接使用這些類別來自行分配記憶體,例如從資源載入點陣圖,或是您想將指標的記憶體分配到原始類型。若要瞭解相關操作方式,可參閱分配記憶體並繫結至 RenderScript一節。下表詳細說明三個記憶體管理類別:

Android 物件類型 說明
Element

元素指的是記憶體配置的一個細胞元,具有兩種形式:基本或複雜。

基本元素包含任何有效 RenderScript 資料類型的單一資料元件。基本元素資料類型的範例包括單一 float 值、float4 向量或單一 RGB-565 色彩。

複雜元素包含基本元素清單,並依據您在 RenderScript 程式碼中宣告的 struct 建立。例如,一個配置方式可包含多個在記憶體中依序排列的 struct。系統會將每個結構視為專屬元素,而不是該結構中的各個資料類型。

Type

類型是記憶體配置範本,由元素和一或多個維度組成。其中會描述記憶體的布局 (基本上是 Element 的陣列),但不會為所描述的資料分配記憶體。

類型由五個維度組成:X、Y、Z、LOD (詳細程度) 和 Face (立方體對應體)。您可將 X、Y、Z 維度設為可用記憶體限制下的正整數。單一維度配置的 X 維度大於零,Y 和 Z 維度為零,則代表不存在。例如,我們將 x=10 和 y=1 的配置視為兩個維度,並將 x=10 和 y=0 視為一個維度。LOD 和 Face 維度是布林值,用於表示存在或不存在。

Allocation

配置方式會根據 Type 代表的記憶體描述,為應用程式提供記憶體。分配的記憶體可同時存於多個記憶體空間中。如果修改一個空間中的記憶體,就必須明確同步處理記憶體,這樣才能在存有記憶體的其他所有空間中更新記憶體。

配置方式資料主要透過以下兩種方式上傳:已檢查類型和未檢查類型。若是簡單陣列,可使用 copyFrom() 函式,從 Android 系統中擷取陣列,並將其複製到原生層記憶體存放區。未檢查類型可讓 Android 系統複製結構陣列,因為其不支援結構。例如,如果某個配置方式是 n 個浮點的陣列,則可複製包含在 float[n] 陣列或 byte[n*4] 陣列中的資料。

處理記憶體

您在 RenderScript 中宣告的非靜態全域變數會在編譯期間分配記憶體。您可直接在 RenderScript 程式碼中處理這些變數,無須在 Android 架構層級為這些變數分配記憶體。另外,Android 架構層也可以利用在反映層類別中產生的現有存取子方法,存取這些變數。如果這些變數已在 RenderScript 執行階段層進行初始化,這些值會用於初始化 Android 架構層中的對應值。如果將全域變數標示為 const,則不會產生 set 方法。詳情請參閱此處

注意:如果您使用的特定 RenderScript 結構包含指標 (例如 rs_program_fragmentrs_allocation),就必須取得對應 Android 架構類別的物件,然後呼叫該結構的 set 方法,將記憶體繫結至 RenderScript 執行階段。您無法在 RenderScript 執行階段層直接操控這些結構。這項限制不適用於包含指標的使用者定義結構,因為一開始就無法將其匯出至反映層類別。如果您嘗試宣告包含指標的非靜態全域結構,就會產生編譯器錯誤。

RenderScript 也支援指標,但您必須在 Android 架構程式碼中明確分配記憶體。在 .rs 檔案中宣告全域指標時,需要透過適當的反映層類別分配記憶體,並將該記憶體繫結至原生 RenderScript 層。您可從 Android 架構層與 RenderScript 層與此記憶體互動,讓您有足夠彈性在最適當的層中修改變數。

分配動態記憶體並繫結至 RenderScript

若要分配動態記憶體,最常見的方法就是呼叫 Script.FieldBase 類別的建構函式。或者也可手動建立 Allocation,這對原始類型指標不可或缺。為方便起見,最好使用 Script.FieldBase 類別的建構函式。取得記憶體配置後,請呼叫指標的反映 bind 方法,將分配的記憶體繫結至 RenderScript 執行階段。

以下範例會分配原始類型指標 intPointer 和指向結構的指標 touchPoints 的記憶體。同時將記憶體繫結至 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);

   ...
}

讀取和寫入記憶體

您可以在 RenderScript 執行階段和 Android 架構層,讀取和寫入靜態與動態分配的記憶體。

靜態分配的記憶體具備 RenderScript 執行階段層級的單向通訊限制。當 RenderScript 程式碼變更變數的值時,為了保障效率,不會將此事傳回 Android 架構層。呼叫 get 方法時,系統一律傳回在 Android 架構中設定的最後一個值。不過,當 Android 架構程式碼修改變數時,可將該變化自動傳送至 RenderScript 執行階段,或稍後再同步處理。如需將 RenderScript 執行階段的資料傳送至 Android 架構層,可使用 rsSendToClient() 函式來突破這項限制。

處理動態分配的記憶體時,如果使用相關指標修改記憶體配置,RenderScript 執行階段層的任何變化都會傳回 Android 架構層。在 Android 架構層修改物件後,該變化會立即傳回 RenderScript 執行階段層。

讀取和寫入全域變數

讀取和寫入全域變數是相當直接的過程。您可在 Android 架構層級使用存取子方法,或直接在 RenderScript 程式碼中設定。請留意,您在 RenderScript 程式碼中所做的任何變更,都不會傳回 Android 架構層 (詳情請參閱此處)。

例如,假設已在名為 rsfile.rs 的檔案中宣告下列結構:

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

Point_t point;

您可像這樣直接在 rsfile.rs 中,將值指派至結構。這些值不會傳回 Android 架構層級:

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

您可像這樣在 Android 架構層,將值指派至結構。這些值會以非同步的方式傳回 RenderScript 執行階段層級:

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

您可像這樣讀取 RenderScript 程式碼中的值:

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

您可利用下列程式碼讀取 Android 架構層中的值。請留意,只有在 Android 架構層級設定一個值後,此程式碼才會回傳值。如果只在 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());

讀取和寫入全域指標

假設已在 Android 架構層級分配記憶體,並繫結至 RenderScript 執行階段,您可使用該指標的 getset 方法,讀取和寫入 Android 架構層級中的記憶體。與靜態分配的記憶體不同,在 RenderScript 執行階段層中,您可像平常一樣讀取和寫入含指標的記憶體,而且變更內容會傳回 Android 架構層。

以指向名為 rsfile.rs 的檔案中的 struct 的下列指標為例:

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

Point_t *point;

假設您已經在 Android 架構層分配記憶體,則可像平常一樣存取 struct 中的值。您透過其指標變數對結構所做的任何變更,都會自動適用於 Android 架構層:

Kotlin

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

Java

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

您還可以在 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);

記憶體繫結完成後,每次變更值時,就無須將記憶體重新繫結至 RenderScript 執行階段。