উন্নত RenderScript

RenderScript ব্যবহার করে এমন অ্যাপ্লিকেশন এখনও Android VM-এর মধ্যে চলে, তাই আপনি পরিচিত সব ফ্রেমওয়ার্ক API অ্যাক্সেস করতে পারবেন, তবে উপযুক্ত হলে RenderScript ব্যবহার করতে পারবেন। ফ্রেমওয়ার্ক ও RenderScript রানটাইমের মধ্যে এই ইন্টার‍্যাকশন সহজ করতে, কোডের একটি ইন্টারমিডিয়েট লেয়ারও উপস্থিত থাকে যা কোডের দুটি লেভেলের মধ্যে যোগাযোগ ও মেমোরি ম্যানেজমেন্ট সহজ করে। এই ডকুমেন্টটি কোডের এই বিভিন্ন লেয়ারের পাশাপাশি Android VM এবং RenderScript রানটাইমের মধ্যে কীভাবে মেমরি শেয়ার করা হয় সে সম্পর্কে আরও বিস্তারিত আলোচনা করে।

RenderScript রানটাইম লেয়ার

আপনার RenderScript কোড কম্পাইল করা হয় এবং কমপ্যাক্ট ও সুনির্দিষ্ট রানটাইম লেয়ারে এক্সিকিউট করা হয়। RenderScript রানটাইম API, ইনটেনসিভ কম্পিউটেশনের জন্য সহায়তা প্রদান করে যা পোর্টেবল এবং প্রসেসরে উপলভ্য কোরের সংখ্যা অনুযায়ী অটোমেটিক স্কেলেবল।

মনে রাখবেন: NDK-তে স্ট্যান্ডার্ড C ফাংশন অবশ্যই CPU-তে চালানোর জন্য গ্যারান্টিযুক্ত হতে হবে, তাই 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;
}
  

স্ট্রাক্ট

Structs তাদের নিজস্ব ক্লাসে প্রতিফলিত হয়, যা <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, সংজ্ঞায়িত করে, যা আপনাকে এই মেমরি অ্যালোকেশনের মেমরি স্পেস নির্দিষ্ট করতে দেয়। মেমরি স্পেসের চারটি সম্ভাবনা আছে:
    • USAGE_SCRIPT: স্ক্রিপ্ট মেমরি স্পেসে অ্যালোকেট করে। আপনি মেমরি স্পেস উল্লেখ না করলে, এটি ডিফল্ট মেমরি স্পেস হিসেবে ব্যবহার করা হয়।
    • USAGE_GRAPHICS_TEXTURE: GPU-এর টেক্সচার মেমরি স্পেসে অ্যালোকেট করে।
    • USAGE_GRAPHICS_VERTEX: GPU-এর ভার্টেক্স মেমরি স্পেসে অ্যালোকেট করে।
    • USAGE_GRAPHICS_CONSTANTS: GPU-এর কনস্ট্যান্ট মেমরি স্পেসে অ্যালোকেট করে যা বিভিন্ন প্রোগ্রাম অবজেক্ট ব্যবহার করে।

    বিটওয়াইজ OR অপারেটর ব্যবহার করে আপনি একাধিক মেমরি স্পেস নির্দিষ্ট করতে পারবেন। এটি করলে RenderScript রানটাইমকে বিজ্ঞপ্তি দেয় যে আপনি নির্দিষ্ট মেমরি স্পেসে ডেটা অ্যাক্সেস করতে চান। নিম্নলিখিত উদাহরণটি স্ক্রিপ্ট এবং ভার্টেক্স মেমরি স্পেস উভয় ক্ষেত্রেই একটি কাস্টম ডেটা টাইপের জন্য মেমরি বরাদ্দ করে:

    Kotlin

    val touchPoints: ScriptField_Point = ScriptField_Point(
            myRenderScript,
            2,
            Allocation.USAGE_SCRIPT or Allocation.USAGE_GRAPHICS_VERTEX
    )

    জাভা

    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 নির্দিষ্ট করে যেটি আপনি পড়তে বা লিখতে চান। প্রতিটি সেটার পদ্ধতিতে একটি copyNow প্যারামিটারও রয়েছে যা এই মেমরিটি অবিলম্বে RenderScript রানটাইমে সিঙ্ক করতে হবে কিনা তা নির্দিষ্ট করে। সিঙ্ক করা হয়নি এমন কোনও মেমরি সিঙ্ক করতে, copyAll() কল করুন।
  • createElement() পদ্ধতি মেমরিতে স্ট্রাক্টের বিবরণ তৈরি করে। এই বিবরণটি এক বা একাধিক এলিমেন্ট নিয়ে গঠিত মেমরি বরাদ্দ করতে ব্যবহার করা হয়।
  • resize(), C-তে realloc()-এর মতোই কাজ করে, যা আপনাকে আগে বরাদ্দ করা মেমরি বাড়াতে দেয় এবং আগে তৈরি করা বর্তমান ভ্যালু বজায় রাখে।
  • copyAll() ফ্রেমওয়ার্ক লেভেলে সেট করা মেমরিকে RenderScript রানটাইমে সিঙ্ক করে। আপনি যখন কোনও মেম্বারের উপর সেট অ্যাক্সেসর মেথড কল করেন, তখন একটি ঐচ্ছিক copyNow বুলিয়ান প্যারামিটার থাকে যা আপনি নির্দিষ্ট করতে পারেন। আপনি পদ্ধতিটি কল করলে true নির্দিষ্ট করা মেমরি সিঙ্ক্রোনাইজ করে। আপনি false উল্লেখ করলে, আপনি একবার copyAll() কল করতে পারবেন এবং এটি এখনও সিঙ্ক করা হয়নি এমন সব প্রপার্টির জন্য মেমরি সিঙ্ক করে।

পয়েন্টার

গ্লোবাল পয়েন্টার স্ক্রিপ্ট ক্লাসেই দেখানো হয়, যা project_root/gen/package/name/ScriptC_renderscript_filename-এ অবস্থিত। আপনি struct বা কাজ করে এমন যেকোনও RenderScript টাইপের পয়েন্টার ঘোষণা করতে পারেন, কিন্তু struct-এ পয়েন্টার বা নেস্টেড অ্যারে থাকতে পারবে না। যেমন, আপনি যদি struct এবং int32_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 কোডের জন্য স্ট্যাটিক্যালি অ্যালোকেট করা মেমরি অটোমেটিক তৈরি হয়। মেমরি অ্যালোকেশন কীভাবে হয় সেই সম্পর্কে আরও তথ্য পেতে ছবি ১ দেখুন।

এই মেমরি অ্যালোকেশন সিস্টেমকে সাপোর্ট করার জন্য, এক সেট API আছে যা Android VM-কে মেমরি অ্যালোকেট করতে এবং malloc কলের মতো একই ধরনের কার্যকারিতা অফার করতে দেয়। এই ক্লাসগুলি মূলত বর্ণনা করে যে কীভাবে মেমরি বরাদ্দ করা উচিত এবং বরাদ্দও করে। এই ক্লাসগুলি কীভাবে কাজ করে তা আরও ভালভাবে বুঝতে, একটি সাধারণ malloc কলের সাথে সম্পর্কিত করে সেগুলি সম্পর্কে চিন্তা করা কার্যকর, যা এইরকম দেখতে হতে পারে:

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

malloc কলকে দুটি ভাগে ভাগ করা যেতে পারে: মেমরির সাইজ যা বরাদ্দ করা হচ্ছে (sizeof(int)), তার সাথে সেই মেমরির কত ইউনিট বরাদ্দ করা উচিত (১০)। Android ফ্রেমওয়ার্ক এই দুটি অংশের জন্য ক্লাস প্রদান করে, তার সাথে malloc-কে দেখানোর জন্য একটি ক্লাসও প্রদান করে।

Element ক্লাস malloc কলের (sizeof(int)) অংশকে প্রতিনিধিত্ব করে এবং মেমরি অ্যালোকেশনের একটি সেলকে এনক্যাপসুলেট করে, যেমন একটি ফ্লোট ভ্যালু বা স্ট্রাক্ট। Type ক্লাস Element এবং বরাদ্দ করার জন্য এলিমেন্টের পরিমাণ (আমাদের উদাহরণে ১০) এনক্যাপসুলেট করে। আপনি 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 (লেভেল অফ ডিটেল) এবং ফেস (কিউব ম্যাপের)। আপনি উপলভ্য মেমরির সীমাবদ্ধতার মধ্যে যেকোনও ধনাত্মক পূর্ণ সংখ্যায় X,Y,Z মাত্রা সেট করতে পারবেন। একটি একক ডাইমেনশন অ্যালোকেশনে X ডাইমেনশন শূন্যের থেকে বেশি এবং Y ও Z ডাইমেনশন শূন্য হয়, যা বোঝায় যে সেগুলি উপস্থিত নেই। যেমন, x=10, y=1-এর মতো বরাদ্দকে দ্বিমাত্রিক এবং x=10, y=0-এর মতো বরাদ্দকে একমাত্রিক হিসেবে বিবেচনা করা হয়। LOD এবং Faces ডাইমেনশন হল বুলিয়ান যা দেখায় যে ডেটা আছে নাকি নেই।

Allocation

অ্যালোকেশন, মেমরির বিবরণের উপর ভিত্তি করে অ্যাপ্লিকেশনের জন্য মেমরি প্রদান করে যা Type দ্বারা উপস্থাপিত হয়। অ্যালোকেট করা মেমরি একই সাথে একাধিক মেমরি স্পেসে থাকতে পারে। একটি স্পেসে মেমরি পরিবর্তন করা হলে, আপনাকে অবশ্যই স্পষ্টভাবে মেমরি সিঙ্ক করতে হবে, যাতে এটি অন্য সব স্পেসে আপডেট করা হয় যেখানে এটি আছে।

অ্যালোকেশন ডেটা দুটি প্রাথমিক উপায়ের একটিতে আপলোড করা হয়: টাইপ চেক করা এবং টাইপ আনচেক করা। সহজ অ্যারের জন্য copyFrom()টি ফাংশন আছে যা Android সিস্টেম থেকে অ্যারে নেয় এবং নেটিভ লেয়ার মেমরি স্টোরে কপি করে। আনচেক করা ভেরিয়েন্ট Android সিস্টেমকে স্ট্রাকচারের অ্যারে কপি করার অনুমতি দেয় কারণ এটি স্ট্রাকচার সাপোর্ট করে না। যেমন, n ফ্লোটের অ্যারে হিসেবে কোনও অ্যালোকেশন থাকলে, float[n] অ্যারে বা byte[n*4] অ্যারেতে থাকা ডেটা কপি করা যেতে পারে।

মেমরি নিয়ে কাজ করা

আপনার RenderScript-এ ঘোষণা করা নন-স্ট্যাটিক, গ্লোবাল ভেরিয়েবেল কম্পাইল করার সময় মেমরি বরাদ্দ করা হয়। Android ফ্রেমওয়ার্ক লেভেলে মেমরি বরাদ্দ না করেই আপনি সরাসরি RenderScript কোডে এইসব ভেরিয়েবল নিয়ে কাজ করতে পারবেন। এছাড়াও, Android ফ্রেমওয়ার্ক লেয়ারের কাছে এইসব ভেরিয়েবলের অ্যাক্সেস আছে যেগুলি রিফ্লেক্টেড লেয়ার ক্লাসে জেনারেট করা অ্যাক্সেসর পদ্ধতির মাধ্যমে প্রদান করা হয়। RenderScript রানটাইম লেয়ারে এই ভেরিয়েবেলগুলি ইনিশিয়ালাইজ করা হলে, Android ফ্রেমওয়ার্ক লেয়ারে সংশ্লিষ্ট ভ্যালুগুলি ইনিশিয়ালাইজ করতে সেই ভ্যালুগুলি ব্যবহার করা হয়। গ্লোবাল ভেরিয়েবেল const হিসেবে চিহ্নিত করা হলে, set মেথড তৈরি হয় না। আরও বিবরণের জন্য এখানে দেখুন।

মনে রাখবেন: আপনি যদি এমন কিছু RenderScript স্ট্রাকচার ব্যবহার করেন যাতে পয়েন্টার থাকে, যেমন rs_program_fragment এবং rs_allocation, তাহলে আপনাকে প্রথমে সংশ্লিষ্ট Android ফ্রেমওয়ার্ক ক্লাসের একটি অবজেক্ট পেতে হবে এবং তারপর RenderScript রানটাইমে মেমরি বাইন্ড করার জন্য সেই স্ট্রাকচারের জন্য set পদ্ধতি কল করতে হবে। RenderScript রানটাইম লেয়ারে আপনি সরাসরি এইসব স্ট্রাকচার ম্যানিপুলেট করতে পারবেন না। এই বিধিনিষেধ ব্যবহারকারীর দ্বারা সংজ্ঞায়িত স্ট্রাকচারের ক্ষেত্রে প্রযোজ্য নয় যেখানে পয়েন্টার থাকে, কারণ সেগুলি প্রথমত রিফ্লেক্টেড লেয়ার ক্লাসে এক্সপোর্ট করা যায় না। আপনি যদি একটি নন-স্ট্যাটিক, গ্লোবাল স্ট্রাক্ট ঘোষণা করার চেষ্টা করেন যাতে একটি পয়েন্টার থাকে, তাহলে একটি কম্পাইলার ত্রুটি তৈরি হয়।

RenderScript-এ পয়েন্টারের জন্য সাপোর্টও আছে, তবে আপনাকে অবশ্যই আপনার Android ফ্রেমওয়ার্ক কোডে মেমরি স্পষ্টভাবে বরাদ্দ করতে হবে। আপনার .rs ফাইলে গ্লোবাল পয়েন্টার ঘোষণা করার সময়, আপনি উপযুক্ত রিফ্লেক্টেড লেয়ার ক্লাসের মাধ্যমে মেমরি বরাদ্দ করেন এবং সেই মেমরিকে নেটিভ RenderScript লেয়ারের সাথে বাইন্ড করেন। আপনি Android ফ্রেমওয়ার্ক লেয়ারের পাশাপাশি RenderScript লেয়ার থেকেও এই মেমরির সাথে ইন্টার‍্যাক্ট করতে পারবেন, যা আপনাকে সবচেয়ে উপযুক্ত লেয়ারে ভেরিয়েবল পরিবর্তন করার সুবিধা দেয়।

RenderScript-এ ডায়নামিক মেমরি বরাদ্দ ও বাইন্ড করা

ডায়নামিক মেমরি বরাদ্দ করতে, আপনাকে Script.FieldBase ক্লাসের কনস্ট্রাক্টর কল করতে হবে, যা সবচেয়ে সাধারণ উপায়। বিকল্প হিসেবে, ম্যানুয়ালি একটি Allocation তৈরি করতে পারেন, যা প্রিমিটিভ টাইপ পয়েন্টারের মতো জিনিসের জন্য প্রয়োজন হয়। সহজ করার জন্য, যখনই উপলভ্য হবে, আপনার Script.FieldBase ক্লাস কনস্ট্রাক্টর ব্যবহার করা উচিত। মেমরি অ্যালোকেশন পাওয়ার পরে, অ্যালোকেট করা মেমরিকে RenderScript রানটাইমের সাথে বাইন্ড করতে পয়েন্টারের রিফ্লেক্ট করা bind মেথড কল করুন।

নিচের উদাহরণে প্রিমিটিভ টাইপ পয়েন্টার, 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)

   ...
}

জাভা

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 ফ্রেমওয়ার্ক লেয়ারে তা আবার জানানো হয় না। Android ফ্রেমওয়ার্ক থেকে সেট করা শেষ ভ্যালু সবসময় get মেথড কল করার সময় রিটার্ন করা হয়। তবে, 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
}

জাভা

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

জাভা

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 রানটাইমে বাউন্ড করা হয়েছে বলে ধরে নিয়ে, আপনি সেই পয়েন্টারের জন্য get এবং set পদ্ধতি ব্যবহার করে 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
}

জাভা

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)

জাভা

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 রানটাইমে মেমরি আবার বাউন্ড করতে হয় না।