Gelişmiş RenderScript

RenderScript'i kullanan uygulamalar hâlâ Android sanal makinesinin içinde çalıştığı için aşina olduğunuz tüm çerçeve API'lerine erişebilirsiniz ancak uygun olduğunda RenderScript'i kullanabilirsiniz. Çerçeve ve RenderScript çalışma zamanı arasındaki bu etkileşimi kolaylaştırmak için, iki kod düzeyi arasındaki iletişimi ve bellek yönetimini kolaylaştıran bir ara kod katmanı da mevcuttur. Bu dokümanda, farklı kod katmanları ve belleğin Android sanal makinesi ile RenderScript çalışma zamanı arasında nasıl paylaşıldığı hakkında daha ayrıntılı bilgiler verilmektedir.

RenderScript Çalışma Zamanı Katmanı

RenderScript kodunuz derlenip kompakt ve iyi tanımlanmış bir çalışma zamanı katmanında yürütülür. RenderScript çalışma zamanı API'leri, taşınabilir ve işlemcideki çekirdek miktarına göre otomatik olarak ölçeklendirilebilen yoğun hesaplama desteği sunar.

Not: NDK'daki standart C işlevlerinin CPU'da çalışacağı garanti edilmelidir. Bu nedenle RenderScript, farklı işlemci türleri üzerinde çalışacak şekilde tasarlandığından RenderScript bu kitaplıklara erişemez.

RenderScript kodunuzu Android projenizin src/ dizinindeki .rs ve .rsh dosyalarında tanımlarsınız. Kod, bir Android derlemesinin parçası olarak çalışan llvm derleyicisi tarafından ara bayt koduna göre derlenir. Uygulamanız bir cihazda çalıştığında, bayt kodu daha sonra cihazda bulunan başka bir llvm derleyicisi tarafından makine koduna derlenir (tam zamanında). Makine kodu cihaz için optimize edilir ve ayrıca önbelleğe alınır. Böylece, RenderScript'in etkinleştirildiği uygulamanın sonraki kullanımları bayt kodunu yeniden derlemez.

RenderScript çalışma zamanı kitaplıklarının bazı temel özellikleri şunlardır:

  • Bellek ayırma isteği özellikleri
  • Birçok yaygın rutinin hem skaler hem de vektör türünde yazılmış aşırı yüklenmiş versiyonlarını içeren geniş bir matematik fonksiyonları koleksiyonu. Toplama, çarpma, çarpım ve çarpım gibi işlemlerin yanı sıra atomik aritmetik ve karşılaştırma işlevleri de kullanılabilir.
  • Basit veri türleri ve vektörler, matris rutinleri ve tarih ile saat rutinleri için dönüşüm rutinleri
  • İki, üç veya dört vektörü tanımlamak için Vector türleri gibi RenderScript sistemini destekleyen veri türleri ve yapıları.
  • Günlük kaydı işlevleri

Kullanılabilir işlevler hakkında daha fazla bilgi için RenderScript çalışma zamanı API'sı referansına bakın.

Yansıtılan Katman

Yansıtılan katman, Android derleme araçlarının Android çerçevesinden RenderScript çalışma zamanına erişim sağlamak için oluşturduğu sınıf kümesidir. Bu katman, RenderScript kodunuzda tanımlanan işaretçiler için bellek ayırmanıza ve bellekle çalışmanıza olanak tanıyan yöntemler ve oluşturucular da sağlar. Aşağıdaki listede, yansıtılan başlıca bileşenler açıklanmaktadır:

  • Oluşturduğunuz her .rs dosyası, ScriptC türünde project_root/gen/package/name/ScriptC_renderscript_filename adlı bir sınıf halinde oluşturulur. Bu dosya, .rs dosyanızın Android çerçevesinden çağırabileceğiniz .java sürümüdür. Bu sınıf, .rs dosyasından yansıtılan aşağıdaki öğeleri içerir:
    • Statik olmayan işlevler
    • Statik olmayan, global RenderScript değişkenleri. Her değişken için erişimci yöntemleri oluşturulur. Böylece, RenderScript değişkenlerini Android çerçevesinden okuyup yazabilirsiniz. Bir genel değişken RenderScript çalışma zamanı katmanında başlatılırsa bu değerler, Android çerçeve katmanındaki karşılık gelen değerleri başlatmak için kullanılır. Genel değişkenler const olarak işaretlenirse set yöntemi oluşturulmaz. Ayrıntılı bilgi için buraya göz atın.

    • Global işaretçiler
  • struct, Script.FieldBase aralığını genişleten project_root/gen/package/name/ScriptField_struct_name adlı kendi sınıfına yansıtılır. Bu sınıf, bir struct dizisini temsil eder. Bu sayede, bu struct örneğinin bir veya daha fazla örneği için bellek tahsis edebilirsiniz.

Fonksiyonlar

İşlevler, project_root/gen/package/name/ScriptC_renderscript_filename bölgesinde bulunan komut dosyası sınıfına yansıtılır. Örneğin, RenderScript kodunuzda aşağıdaki işlevi tanımlarsanız:

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

şu Java kodu oluşturulur:

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 sistemi eşzamansız olacak şekilde tasarlandığından, işlevler dönüş değerlerine sahip olamaz. Android çerçeve kodunuz RenderScript'e çağrı yaptığında çağrı sıraya alınır ve mümkün olduğunda yürütülür. Bu kısıtlama, RenderScript sisteminin sürekli kesinti olmadan çalışmasını sağlar ve verimliliği artırır. Fonksiyonların döndürülen değerlere sahip olmasına izin verilmişse çağrı, değer döndürülene dek engellenir.

RenderScript kodunun Android çerçevesine bir değer göndermesini istiyorsanız rsSendToClient() işlevini kullanın.

Değişkenler

Desteklenen türlerin değişkenleri, project_root/gen/package/name/ScriptC_renderscript_filename konumunda bulunan komut dosyası sınıfının kendisine yansıtılır. Her değişken için bir erişim yöntemi grubu oluşturulur. Örneğin, RenderScript kodunuzda aşağıdaki değişkeni tanımlarsanız:

uint32_t unsignedInteger = 1;

şu Java kodu oluşturulur:

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

public long get_unsignedInteger(){
    return mExportVar_unsignedInteger;
}
  

Yapılar

Yapılar, <project_root>/gen/com/example/renderscript/ScriptField_struct_name bölgesinde bulunan kendi sınıflarına yansıtılır. Bu sınıf, bir struct dizisini temsil eder ve belirtilen sayıda struct için bellek ayırmanıza olanak tanır. Örneğin, aşağıdaki struct'ı tanımlarsanız:

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

aşağıdaki kod ScriptField_Point.java ile oluşturulur:

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

Oluşturulan kod, RenderScript çalışma zamanı tarafından istenen struct'lara bellek ayırmaya ve bellekteki struct'lerle etkileşim kurmaya yardımcı olmak için size kolaylık sağlamak amacıyla sağlanır. Her struct sınıfı aşağıdaki yöntemleri ve kurucuları tanımlar:

  • Belleği ayırmanıza olanak tanıyan aşırı yüklenmiş kurucular. ScriptField_struct_name(RenderScript rs, int count) oluşturucu, count parametresiyle bellek ayırmak istediğiniz yapıların sayısını tanımlamanıza olanak tanır. ScriptField_struct_name(RenderScript rs, int count, int usages) oluşturucu, bu bellek ayırmanın bellek alanını belirtmenize olanak tanıyan ek bir parametre (usages) tanımlar. Dört bellek alanı olasılığı vardır:

    Bit tabanlı OR operatörünü kullanarak birden fazla bellek alanı belirtebilirsiniz. Bu işlem, RenderScript çalışma zamanına belirtilen bellek alanlarındaki verilere erişmeyi düşündüğünüzü bildirir. Aşağıdaki örnek, hem komut dosyası hem de köşe bellek alanlarında özel bir veri türü için bellek tahsis eder:

    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);
    
  • İç içe yerleştirilmiş statik bir sınıf (Item), struct öğesinin bir örneğini nesne biçiminde oluşturmanıza olanak tanır. İç içe yerleştirilmiş bu sınıf, Android kodunuzda struct ile çalışmak daha mantıklıysa yararlıdır. Nesneyle ilgili işlemleri tamamladığınızda set(Item i, int index, boolean copyNow) yöntemini çağırarak ve Item öğesini dizide istediğiniz konuma ayarlayarak nesneyi ayrılan belleğe aktarabilirsiniz. RenderScript çalışma zamanı, yeni yazılan belleğe otomatik olarak erişim sağlar.
  • Bir struct içindeki her alanın değerlerini almak ve ayarlamak için erişimci yöntemleri. Bu erişimci yöntemlerinin her biri, dizide okumak veya yazmak istediğiniz struct öğesini belirtmek için bir index parametresine sahiptir. Her setter yöntemi, bu belleğin RenderScript çalışma zamanıyla hemen senkronize edilip edilmeyeceğini belirten bir copyNow parametresine de sahiptir. Senkronize edilmemiş anıları senkronize etmek için copyAll() numaralı telefonu arayın.
  • createElement() yöntemi, bellekteki struct için bir açıklama oluşturur. Bu açıklama, bir veya daha fazla öğeden oluşan belleği ayırmak için kullanılır.
  • resize(), C'deki bir realloc() gibi çalışır. Bu sayede, önceden ayrılmış belleği genişleterek daha önce oluşturulan mevcut değerleri koruyabilirsiniz.
  • copyAll(), çerçeve düzeyinde ayarlanan belleği RenderScript çalışma zamanıyla senkronize eder. Bir üyede ayarlanmış erişimci yöntemini çağırdığınızda belirtebileceğiniz isteğe bağlı bir copyNow boole parametresi vardır. true belirtilmesi, yöntemi çağırırken belleği senkronize eder. Yanlış değerini belirtirseniz copyAll() öğesini bir kez çağırabilirsiniz. Bu durumda işlem, henüz senkronize edilmemiş tüm özelliklerin belleğini senkronize eder.

Nokta Doğrulayıcılar

Global işaretçiler, project_root/gen/package/name/ScriptC_renderscript_filename konumunda bulunan komut dosyası sınıfının kendisine yansıtılır. Bir struct veya desteklenen RenderScript türlerinden herhangi birine işaretçiler bildirebilirsiniz ancak struct, işaretçiler veya iç içe yerleştirilmiş diziler içeremez. Örneğin, aşağıdaki işaretçileri bir struct ve int32_t öğeleri için tanımlarsanız

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

Point_t *touchPoints;
int32_t *intPointer;

şu Java kodu oluşturulur:

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

Bir get yöntemi ve bind_pointer_name adlı özel bir yöntem (set() yöntemi yerine) oluşturulur. bind_pointer_name yöntemi, Android sanal makinesinde ayrılan belleği RenderScript çalışma zamanına bağlamanıza olanak tanır (.rs dosyanızda belleği ayıramazsınız). Daha fazla bilgi için Ayrılan Bellekle Çalışma bölümüne bakın.

Bellek Ayırma API'leri

RenderScript kullanan uygulamalar Android sanal makinesinde çalışmaya devam eder. Ancak gerçek RenderScript kodu yerel olarak çalışır ve Android sanal makinesinde ayrılan belleğe erişmesi gerekir. Bunu yapmak için sanal makinede ayrılan belleği RenderScript çalışma zamanına eklemeniz gerekir. Bağlama adı verilen bu işlem, RenderScript çalışma zamanının istediği ancak açıkça ayıramadığı bellekle sorunsuz bir şekilde çalışmasına olanak tanır. Sonuç, temelde C'de malloc ifadesini oluşturmakla aynıdır. Ek faydası, Android sanal makinesinin çöp toplama işlemi gerçekleştirebilmesi ve RenderScript çalışma zamanı katmanıyla belleği paylaşabilmesidir. Bağlama yalnızca dinamik olarak ayrılmış bellek için gereklidir. RenderScript kodunuz için derleme sırasında statik olarak ayrılmış bellek otomatik olarak oluşturulur. Bellek ayırmanın nasıl gerçekleştiği hakkında daha fazla bilgi için Şekil 1'e bakın.

Bu bellek ayırma sistemini desteklemek için Android sanal makinesinin belleği ayırmasına ve bir malloc çağrısına benzer işlevler sunmasına olanak tanıyan API'ler mevcuttur. Bu sınıflar temel olarak belleğin nasıl paylaştırılması gerektiğini açıklar ve ayrıca ayırmayı yürütür. Bu sınıfların işleyiş şeklini daha iyi anlamak için sınıfları aşağıdaki gibi basit bir malloc çağrısıyla kıyaslamak faydalı olabilir:

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

malloc çağrısı iki bölüme ayrılabilir: Ayrılan belleğin boyutu (sizeof(int)) ve bu belleğin kaç biriminin ayrılması gerektiği (10). Android çerçevesi, bu iki kısım için sınıflar ve malloc kendisini temsil eden bir sınıf sağlar.

Element sınıfı, malloc çağrısının (sizeof(int)) kısmını temsil eder ve tek bir kayan değer değeri veya struct gibi bellek ayırmanın bir hücresini içerir. Type sınıfı, Element öğesini ve ayrılacak öğe miktarını içerir (örneğimizde 10). Type değerini, Element dizisi olarak düşünebilirsiniz. Allocation sınıfı, belirli bir Type öğesine göre gerçek bellek tahsisini yapar ve ayrılan gerçek belleği temsil eder.

Çoğu durumda, bu bellek ayırma API'lerini doğrudan çağırmanız gerekmez. Yansıtılan katman sınıfları, bu API'leri otomatik olarak kullanacak kodu oluşturur. Belleği ayırmak için tek yapmanız gereken, yansıtılan katman sınıflarından birinde tanımlanmış bir kurucu çağırmak ve ardından ortaya çıkan belleği (Allocation) RenderScript'e bağlamaktır. Belleği kendiniz paylaştırmak için bu sınıfları doğrudan kullanmak isteyebileceğiniz durumlar vardır. Örneğin, bir kaynaktan bit eşlem yüklemek veya temel türlere işaretçiler için bellek ayırmak istediğinizde. Bunu nasıl yapacağınızı RenderScript'e bellek ayırma ve bağlama bölümünde öğrenebilirsiniz. Aşağıdaki tabloda, üç bellek yönetimi sınıfı daha ayrıntılı olarak açıklanmaktadır:

Android Nesne Türü Açıklama
Element

Bir öğe, bellek ayırmada bir hücreyi tanımlar ve temel veya karmaşık olmak üzere iki biçime sahip olabilir.

Temel öğe, geçerli herhangi bir RenderScript veri türünün tek bir veri bileşenini içerir. Temel öğe veri türlerine örnek olarak tek bir float değeri, float4 vektörü veya tek bir RGB-565 rengi verilebilir.

Karmaşık öğeler, temel öğelerin bir listesini içerir ve RenderScript kodunuzda bildirdiğiniz struct öğelerinden oluşturulur. Örneğin bir ayırma, bellekte sıraya göre düzenlenmiş birden fazla struct içerebilir. Her struct, içindeki her veri türü yerine kendi öğesi olarak kabul edilir.

Type

Tür, bellek ayırma şablonudur ve bir öğe ile bir veya daha fazla boyuttan oluşur. Hafızanın düzenini (temelde Element dizisi) açıklar ancak belleği açıkladığı verilere ayırmaz.

Bir tür beş boyuttan oluşur: X, Y, Z, LOD (ayrıntı seviyesi) ve Yüzler (küp haritada). X,Y,Z boyutlarını mevcut bellek kısıtlamaları dahilinde herhangi bir pozitif tam sayı değerine ayarlayabilirsiniz. Tek boyut ayırmanın X boyutu sıfırdan büyüktür. Y ve Z boyutları ise sıfırdır ve bunun mevcut olmadığını belirtir. Örneğin, x=10, y=1 ayırması iki boyutlu, x=10, y=0 ise tek boyutlu olarak kabul edilir. LOD ve Yüzler boyutları, mevcut olup olmadığını belirtmek için boole değerleridir.

Allocation

Ayırma, Type ile temsil edilen belleğin açıklamasına göre uygulamalar için belleği sağlar. Ayrılan bellek, aynı anda birçok bellek alanında mevcut olabilir. Hafıza bir alanda değiştirilirse belleğin mevcut olduğu diğer tüm alanlarda güncellenebilmesi için belleği açık bir şekilde senkronize etmeniz gerekir.

Ayırma verileri şu iki temel yöntemden biriyle yüklenir: "işaretli" yazın ve "işaretsiz" yazın. Basit dizilerde, Android sisteminden bir diziyi alıp yerel katman bellek deposuna kopyalayan copyFrom() işlevleri vardır. İşaretlenmemiş varyantlar, yapıları desteklemediği için Android sisteminin yapı dizilerini kopyalamasına olanak tanır. Örneğin, n kayan nokta dizisinden oluşan bir ayırma varsa bir float[n] dizisinde veya byte[n*4] dizisinde yer alan veriler kopyalanabilir.

Bellekle Çalışma

RenderScript'inizde bildirdiğiniz statik olmayan genel değişkenler, derleme zamanında bellek ayrılır. Bu değişkenlerle Android çerçevesi düzeyinde bellek ayırmaya gerek kalmadan doğrudan RenderScript kodunuzda çalışabilirsiniz. Android çerçeve katmanı, yansıtılan katman sınıflarında oluşturulan erişimci yöntemleriyle bu değişkenlere de erişebilir. Bu değişkenler RenderScript çalışma zamanı katmanında başlatılırsa Android çerçeve katmanındaki karşılık gelen değerleri başlatmak için bu değerler kullanılır. Genel değişkenler sabit olarak işaretlenirse set yöntemi oluşturulmaz. Ayrıntılı bilgi için buraya bakın.

Not: rs_program_fragment ve rs_allocation gibi işaretçi içeren belirli RenderScript yapıları kullanıyorsanız önce ilgili Android çerçeve sınıfının bir nesnesini almanız ve ardından belleği RenderScript çalışma zamanına bağlamak için bu yapı için set yöntemini çağırmanız gerekir. Bu yapıları, RenderScript çalışma zamanı katmanından doğrudan değiştiremezsiniz. Bu kısıtlama, işaretçiler içeren kullanıcı tanımlı yapılar için geçerli değildir. Bunun nedeni, bu yapıların öncelikle yansıtılan katman sınıfına dışa aktarılamamasıdır. İşaretçi içeren statik olmayan, global bir struct bildirmeye çalışırsanız derleyici hatası oluşur.

RenderScript, işaretçileri de destekler, ancak belleği Android çerçeve kodunuzda açıkça tahsis etmeniz gerekir. .rs dosyanızda genel bir işaretçi tanımladığınızda, belleği uygun yansıtılan katman sınıfıyla ayırır ve bu belleği yerel RenderScript katmanına bağlarsınız. Bu bellekle hem Android çerçeve katmanından hem de RenderScript katmanından etkileşim kurabilirsiniz. Bu katman, değişkenleri en uygun katmanda değiştirme esnekliği sağlar.

Dinamik belleği RenderScript'e ayırma ve bağlama

Dinamik belleği ayırmak için Script.FieldBase sınıfının oluşturucusunu çağırmanız gerekir. Bu, en yaygın yöntemdir. Alternatif bir yöntem de, temel tür işaretçiler gibi öğeler için gerekli olan bir Allocation öğesini manuel olarak oluşturmaktır. Kolaylık sağlaması açısından mümkün olduğunda Script.FieldBase sınıfı oluşturucu kullanmalısınız. Bellek tahsisi aldıktan sonra, ayrılan belleği RenderScript çalışma zamanına bağlamak için işaretçinin yansıtılan bind yöntemini çağırın.

Aşağıdaki örnekte, hem temel tür işaretçisi (intPointer) hem de bir struct (touchPoints) işaretçisi için bellek tahsis edilir. Ayrıca, belleği RenderScript'e bağlar:

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

   ...
}

Hafızalarda okuma ve yazma

Hem RenderScript çalışma zamanı hem de Android çerçeve katmanında statik ve dinamik olarak ayrılmış bellekte okuma ve yazma işlemleri yapabilirsiniz.

Statik olarak ayrılan bellekte RenderScript çalışma zamanı düzeyinde tek yönlü iletişim kısıtlaması bulunur. RenderScript kodu bir değişkenin değerini değiştirdiğinde, verimlilik amacıyla Android çerçeve katmanına geri iletilmez. Android çerçevesinden ayarlanan son değer, get yöntemine yapılan bir çağrı sırasında her zaman döndürülür. Bununla birlikte, Android çerçeve kodu bir değişkeni değiştirdiğinde, bu değişiklik RenderScript çalışma zamanına otomatik olarak iletilebilir veya daha sonra senkronize edilebilir. RenderScript çalışma zamanından Android çerçeve katmanına veri göndermeniz gerekirse bu sınırlamayı aşmak için rsSendToClient() işlevini kullanabilirsiniz.

Dinamik olarak ayrılan bellekle çalışırken, ilişkili işaretçisini kullanarak bellek ayırmayı değiştirdiyseniz RenderScript çalışma zamanı katmanındaki tüm değişiklikler Android çerçeve katmanına geri aktarılır. Android çerçeve katmanındaki bir nesnenin değiştirilmesi, bu değişikliğin hemen RenderScript çalışma zamanı katmanına geri yayılmasına neden olur.

Genel değişkenlere okuma ve yazma

Global değişkenlere okuma ve yazma işlemleri basit bir süreçtir. Erişimci yöntemlerini Android çerçevesi düzeyinde kullanabilir veya doğrudan RenderScript kodunda ayarlayabilirsiniz. RenderScript kodunuzda yaptığınız değişikliklerin Android çerçeve katmanına geri yayılmayacağını unutmayın (daha ayrıntılı bilgi için buraya bakın).

Örneğin, aşağıdaki yapı rsfile.rs adlı bir dosyada tanımlandığı için:

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

Point_t point;

Bu yapıya doğrudan rsfile.rs içinde değer atayabilirsiniz. Bu değerler, Android çerçevesi düzeyine geri çoğaltılmaz:

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

struct'a Android çerçeve katmanında bunun gibi değerler atayabilirsiniz. Bu değerler, RenderScript çalışma zamanı düzeyine eşzamansız olarak geri gönderilir:

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 kodunuzdaki değerleri şu şekilde okuyabilirsiniz:

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

Android çerçeve katmanındaki değerleri aşağıdaki kodla okuyabilirsiniz. Bu kodun, yalnızca Android çerçevesi düzeyinde ayarlanan bir değer döndürdüğünü unutmayın. Değeri yalnızca RenderScript çalışma zamanı düzeyinde ayarlarsanız boş işaretçi istisnası elde edersiniz:

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

Global işaretçi okuma ve yazma

Belleğin Android çerçevesi düzeyinde ayrıldığı ve RenderScript çalışma zamanına bağlı olduğu varsayıldığında, bu işaretçi için get ve set yöntemlerini kullanarak Android çerçevesi düzeyinde belleği okuyup yazabilirsiniz. RenderScript çalışma zamanı katmanında, işaretçilerle normal şekilde okuyup belleğe yazabilir ve statik olarak ayrılan belleğin aksine değişiklikler Android çerçeve katmanına geri yayılır.

Örneğin, rsfile.rs adlı bir dosyada struct bulunan aşağıdaki işaretçi verilmiştir:

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

Point_t *point;

Android çerçeve katmanında zaten bellek ayırdığınızı varsayarsak struct içindeki değerlere normal şekilde erişebilirsiniz. struct'ta işaretçi değişkeniyle yaptığınız tüm değişiklikler Android çerçeve katmanına otomatik olarak sunulur:

Kotlin

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

Java

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

Ayrıca, Android çerçevesi katmanında işaretçiye değer okuyup yazabilirsiniz:

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

Bellek bağlandıktan sonra, bir değerde her değişiklik yaptığınızda belleği RenderScript çalışma zamanına yeniden bağlamanız gerekmez.