رندر اسکریپت پیشرفته

از آنجا که برنامه‌هایی که از RenderScript استفاده می‌کنند همچنان در داخل Android VM اجرا می‌شوند، شما به تمام APIهای چارچوبی که با آنها آشنا هستید دسترسی دارید، اما می‌توانید در صورت لزوم از RenderScript استفاده کنید. برای تسهیل این تعامل بین چارچوب و زمان اجرا RenderScript، یک لایه میانی از کد نیز برای تسهیل ارتباط و مدیریت حافظه بین دو سطح کد وجود دارد. این سند به جزئیات بیشتری در مورد این لایه‌های مختلف کد و همچنین نحوه اشتراک حافظه بین Android VM و زمان اجرا RenderScript می‌پردازد.

لایه زمان اجرا RenderScript

کد RenderScript شما در یک لایه زمان اجرا فشرده و به خوبی تعریف شده کامپایل و اجرا می شود. API های زمان اجرا RenderScript از محاسبات فشرده پشتیبانی می کنند که قابل حمل است و به طور خودکار به تعداد هسته های موجود در یک پردازنده مقیاس پذیر است.

توجه: توابع استاندارد C در NDK باید تضمین شوند که روی CPU اجرا شوند، بنابراین RenderScript نمی تواند به این کتابخانه ها دسترسی داشته باشد، زیرا RenderScript برای اجرا بر روی انواع مختلف پردازنده ها طراحی شده است.

شما کد RenderScript خود را در فایل های .rs و .rsh در فهرست src/ پروژه اندروید خود تعریف می کنید. کد به بایت کد میانی توسط کامپایلر llvm که به عنوان بخشی از ساخت اندروید اجرا می شود، کامپایل می شود. هنگامی که برنامه شما بر روی یک دستگاه اجرا می شود، بایت کد سپس توسط کامپایلر llvm دیگری که در دستگاه قرار دارد (به موقع) به کد ماشین کامپایل می شود. کد ماشین برای دستگاه بهینه شده است و همچنین در حافظه پنهان ذخیره می شود، بنابراین استفاده های بعدی از برنامه فعال RenderScript، بایت کد را دوباره کامپایل نمی کند.

برخی از ویژگی های کلیدی کتابخانه های زمان اجرا RenderScript عبارتند از:

  • ویژگی های درخواست تخصیص حافظه
  • مجموعه بزرگی از توابع ریاضی با نسخه‌های بارگذاری‌شده اسکالار و بردار بسیاری از روال‌های رایج. عملیاتی مانند جمع، ضرب، حاصل ضرب نقطه‌ای و حاصل ضرب متقاطع و همچنین توابع حساب اتمی و مقایسه در دسترس هستند.
  • روال های تبدیل برای انواع داده های اولیه و بردارها، روال های ماتریسی و روال های تاریخ و زمان
  • انواع داده ها و ساختارهایی برای پشتیبانی از سیستم RenderScript مانند انواع Vector برای تعریف دو، سه یا چهار بردار.
  • توابع ورود به سیستم

برای اطلاعات بیشتر در مورد توابع موجود به مرجع RenderScript Runtime API مراجعه کنید.

لایه بازتاب شده

لایه بازتاب شده مجموعه ای از کلاس هایی است که ابزارهای ساخت اندروید برای دسترسی به زمان اجرا RenderScript از فریم ورک اندروید ایجاد می کنند. این لایه همچنین متدها و سازنده هایی را ارائه می دهد که به شما امکان می دهد برای اشاره گرهایی که در کد RenderScript شما تعریف شده اند، حافظه را تخصیص داده و با آن کار کنید. لیست زیر اجزای اصلی را که منعکس شده اند توضیح می دهد:

  • هر فایل .rs که ایجاد می کنید در کلاسی با نام project_root/gen/package/name/ScriptC_ renderscript_filename از نوع ScriptC تولید می شود. این فایل نسخه .java فایل .rs شما است که می توانید از فریم ورک اندروید آن را فراخوانی کنید. این کلاس حاوی موارد زیر است که از فایل .rs منعکس شده است:
    • توابع غیر استاتیک
    • متغیرهای جهانی RenderScript غیر ایستا. متدهای Accessor برای هر متغیر تولید می‌شوند، بنابراین می‌توانید متغیرهای RenderScript را از چارچوب Android بخوانید و بنویسید. اگر یک متغیر سراسری در لایه زمان اجرا 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;
}

سپس کد جاوا زیر تولید می شود:

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 به صورت ناهمزمان طراحی شده است. وقتی کد فریمورک اندروید شما با RenderScript تماس می گیرد، تماس در صف قرار می گیرد و در صورت امکان اجرا می شود. این محدودیت به سیستم RenderScript اجازه می دهد تا بدون وقفه دائمی کار کند و کارایی را افزایش می دهد. اگر توابع مجاز به داشتن مقادیر بازگشتی باشند، تماس تا زمانی که مقدار برگردانده شود مسدود می شود.

اگر می خواهید کد RenderScript مقداری را به فریم ورک اندروید ارسال کند، از تابع rsSendToClient() استفاده کنید.

متغیرها

متغیرهای انواع پشتیبانی شده در خود کلاس اسکریپت، واقع در project_root/gen/package/name/ScriptC_renderscript_filename منعکس می‌شوند. مجموعه ای از متدهای دسترسی برای هر متغیر تولید می شود. به عنوان مثال، اگر متغیر زیر را در کد RenderScript خود تعریف کنید:

uint32_t unsignedInteger = 1;

سپس کد جاوا زیر تولید می شود:

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 s اختصاص دهید. به عنوان مثال، اگر ساختار زیر را تعریف کنید:

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 s در حافظه در اختیار شما قرار می گیرد. کلاس هر struct متدها و سازنده های زیر را تعریف می کند:

  • سازنده های بیش از حد بارگذاری شده که به شما امکان تخصیص حافظه را می دهند. سازنده ScriptField_ struct_name (RenderScript rs, int count) به شما امکان می دهد تعداد ساختارهایی را که می خواهید حافظه را برای آنها اختصاص دهید با پارامتر count تعریف کنید. سازنده ScriptField_ struct_name (RenderScript rs, int count, int usages) یک پارامتر اضافی به usages تعریف می کند که به شما امکان می دهد فضای حافظه این تخصیص حافظه را مشخص کنید. چهار امکان فضای حافظه وجود دارد:
    • USAGE_SCRIPT : در فضای حافظه اسکریپت تخصیص می دهد. اگر فضای حافظه را مشخص نکنید، این فضای حافظه پیش‌فرض است.
    • USAGE_GRAPHICS_TEXTURE : در فضای حافظه بافت پردازنده گرافیکی تخصیص می دهد.
    • USAGE_GRAPHICS_VERTEX : در فضای حافظه راس GPU تخصیص می دهد.
    • USAGE_GRAPHICS_CONSTANTS : فضای حافظه ثابت GPU را که توسط اشیاء مختلف برنامه استفاده می شود، اختصاص می دهد.

    با استفاده از عملگر بیتی OR می توانید چندین فضای حافظه را مشخص کنید. انجام این کار به زمان اجرای RenderScript اطلاع می دهد که قصد دارید به داده ها در فضاهای حافظه مشخص شده دسترسی داشته باشید. مثال زیر حافظه را برای یک نوع داده سفارشی در هر دو فضای حافظه اسکریپت و راس اختصاص می دهد:

    کاتلین

    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 به شکل یک شی ایجاد کنید. این کلاس تودرتو در صورتی مفید است که کار با struct کد اندروید شما منطقی تر باشد. هنگامی که دستکاری شی تمام شد، می توانید با فراخوانی set(Item i, int index, boolean copyNow) شی را به حافظه اختصاص داده شده فشار دهید و Item در موقعیت دلخواه در آرایه قرار دهید. زمان اجرا RenderScript به طور خودکار به حافظه تازه نوشته شده دسترسی دارد.
  • متدهای Accessor برای دریافت و تنظیم مقادیر هر فیلد در یک ساختار. هر یک از این متدهای دسترسی دارای یک پارامتر index برای تعیین struct آرایه ای است که می خواهید بخوانید یا بنویسید. هر متد تنظیم کننده همچنین دارای یک پارامتر copyNow است که مشخص می کند این حافظه بلافاصله با زمان اجرا RenderScript همگام شود یا خیر. برای همگام‌سازی هر حافظه‌ای که همگام‌سازی نشده است، copyAll() را فراخوانی کنید.
  • متد createElement() شرحی از ساختار در حافظه ایجاد می کند. این توصیف برای تخصیص حافظه متشکل از یک یا چند عنصر استفاده می شود.
  • resize() بسیار شبیه realloc() در C عمل می کند و به شما امکان می دهد حافظه تخصیص داده شده قبلی را گسترش دهید و مقادیر فعلی را که قبلا ایجاد شده بود حفظ کنید.
  • copyAll() حافظه ای را که در سطح چارچوب تنظیم شده بود با زمان اجرا RenderScript همگام می کند. وقتی یک متد set accessor را روی یک عضو فرا می‌خوانید، یک پارامتر بولی 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;

سپس کد جاوا زیر تولید می شود:

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 به شما امکان می دهد حافظه ای را که در ماشین مجازی اندروید اختصاص داده شده است به زمان اجرا RenderScript متصل کنید (شما نمی توانید حافظه را در فایل .rs خود اختصاص دهید). برای اطلاعات بیشتر، به کار با حافظه اختصاص داده شده مراجعه کنید.

APIهای تخصیص حافظه

برنامه‌هایی که از RenderScript استفاده می‌کنند همچنان در VM Android اجرا می‌شوند. با این حال، کد واقعی RenderScript به صورت بومی اجرا می شود و نیاز به دسترسی به حافظه اختصاص داده شده در Android VM دارد. برای انجام این کار، باید حافظه ای را که در VM تخصیص داده شده است به زمان اجرا RenderScript متصل کنید. این فرآیند که binding نام دارد، به زمان اجرا RenderScript اجازه می دهد تا به طور یکپارچه با حافظه ای که درخواست می کند اما نمی تواند به طور صریح تخصیص دهد کار کند. نتیجه نهایی اساساً همان است که شما malloc در C نامیده بودید. مزیت اضافه این است که Android VM می‌تواند جمع‌آوری زباله را انجام دهد و همچنین حافظه را با لایه زمان اجرا RenderScript به اشتراک بگذارد. اتصال فقط برای حافظه تخصیص یافته پویا ضروری است. حافظه تخصیص یافته استاتیک به طور خودکار برای کد RenderScript شما در زمان کامپایل ایجاد می شود. برای اطلاعات بیشتر در مورد نحوه تخصیص حافظه به شکل 1 مراجعه کنید.

برای پشتیبانی از این سیستم تخصیص حافظه، مجموعه‌ای از APIها وجود دارد که به ماشین مجازی اندروید اجازه می‌دهد حافظه را تخصیص دهد و عملکردی مشابه یک تماس malloc ارائه دهد. این کلاس ها اساسا نحوه تخصیص حافظه و همچنین انجام تخصیص را توضیح می دهند. برای درک بهتر نحوه کار این کلاس ها، مفید است که آنها را در رابطه با یک فراخوان ساده malloc که می تواند شبیه به این باشد در نظر بگیرید:

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

فراخوانی malloc را می توان به دو بخش تقسیم کرد: اندازه حافظه اختصاص داده شده ( sizeof(int) )، به همراه تعداد واحدهای آن حافظه که باید تخصیص داده شود (10). فریم ورک اندروید کلاس هایی را برای این دو قسمت و همچنین کلاسی برای نمایش خود malloc فراهم می کند.

کلاس Element بخش ( sizeof(int) ) فراخوانی malloc را نشان می‌دهد و یک سلول از تخصیص حافظه، مانند یک مقدار شناور منفرد یا یک ساختار را کپسوله می‌کند. کلاس Type Element و مقدار عناصری را که باید تخصیص داده شود (در مثال ما 10) کپسوله می کند. شما می توانید یک Type را به عنوان آرایه ای از Element s در نظر بگیرید. کلاس Allocation تخصیص حافظه واقعی را بر اساس Type داده شده انجام می دهد و حافظه تخصیص یافته واقعی را نشان می دهد.

در بیشتر مواقع، نیازی نیست مستقیماً این APIهای تخصیص حافظه را فراخوانی کنید. کلاس‌های لایه بازتاب‌شده کدی را برای استفاده خودکار از این APIها تولید می‌کنند و تنها کاری که برای تخصیص حافظه باید انجام دهید فراخوانی سازنده‌ای است که در یکی از کلاس‌های لایه بازتاب‌شده اعلام شده و سپس Allocation حافظه حاصل را به RenderScript متصل کنید. شرایطی وجود دارد که می‌خواهید مستقیماً از این کلاس‌ها برای تخصیص حافظه به تنهایی استفاده کنید، مانند بارگذاری یک بیت مپ از یک منبع یا زمانی که می‌خواهید حافظه را برای اشاره‌گرها به انواع اولیه اختصاص دهید. نحوه انجام این کار را می توانید در قسمت Allocating and binding memory to RenderScript مشاهده کنید. جدول زیر سه کلاس مدیریت حافظه را با جزئیات بیشتری شرح می دهد:

نوع شی اندروید توضیحات
Element

یک عنصر یک سلول از تخصیص حافظه را توصیف می کند و می تواند دو شکل داشته باشد: پایه یا پیچیده.

یک عنصر اصلی شامل یک جزء واحد از داده‌های هر نوع داده معتبر RenderScript است. نمونه‌هایی از انواع داده‌های عنصر اصلی عبارتند از یک مقدار float واحد، یک بردار float4 یا یک رنگ RGB-565.

عناصر پیچیده حاوی لیستی از عناصر اصلی هستند و از struct که در کد RenderScript خود اعلام می کنید ایجاد می شوند. به عنوان مثال یک تخصیص می تواند شامل struct متعددی باشد که به ترتیب در حافظه مرتب شده اند. هر ساختار به جای هر نوع داده در آن ساختار به عنوان عنصر خاص خود در نظر گرفته می شود.

Type

نوع یک الگوی تخصیص حافظه است و از یک عنصر و یک یا چند بعد تشکیل شده است. چیدمان حافظه را توصیف می کند (در اصل آرایه ای از Element s) اما حافظه را برای داده هایی که توصیف می کند اختصاص نمی دهد.

یک نوع از پنج بعد تشکیل شده است: X، Y، Z، LOD (سطح جزئیات) و چهره ها (نقشه مکعبی). شما می توانید ابعاد X,Y,Z را روی هر عدد صحیح مثبت در محدوده محدودیت های حافظه موجود تنظیم کنید. یک تخصیص تک بعدی دارای بعد X بزرگتر از صفر است در حالی که ابعاد Y و Z صفر هستند تا نشان دهند وجود ندارد. به عنوان مثال، تخصیص x=10، y=1 دو بعدی و x=10، y=0 یک بعدی در نظر گرفته می شود. ابعاد LOD و Faces برای نشان دادن وجود یا عدم وجود، بولی هستند.

Allocation

یک تخصیص، حافظه را برای برنامه‌ها بر اساس توصیفی از حافظه که با یک Type نشان داده می‌شود، فراهم می‌کند. حافظه اختصاص داده شده می تواند در بسیاری از فضاهای حافظه به طور همزمان وجود داشته باشد. اگر حافظه در یک فضا اصلاح شود، باید به صراحت حافظه را همگام کنید تا در تمام فضاهای دیگری که در آن وجود دارد، به روز شود.

داده‌های تخصیص به یکی از دو روش اصلی بارگذاری می‌شوند: تایپ علامت‌گذاری شده و تایپ بدون علامت. برای آرایه‌های ساده، توابع copyFrom() وجود دارد که یک آرایه را از سیستم اندروید می‌گیرند و آن را در ذخیره‌سازی حافظه لایه اصلی کپی می‌کنند. انواع انتخاب نشده به سیستم اندروید اجازه می دهد تا روی آرایه هایی از ساختارها کپی کند زیرا از ساختارها پشتیبانی نمی کند. به عنوان مثال، اگر تخصیصی وجود داشته باشد که آرایه ای از n شناور باشد، داده های موجود در یک آرایه float[n] یا یک آرایه byte[n*4] را می توان کپی کرد.

کار با حافظه

متغیرهای غیر ایستا و سراسری که در RenderScript خود اعلام می کنید در زمان کامپایل به حافظه اختصاص داده می شوند. می‌توانید مستقیماً با این متغیرها در کد RenderScript خود کار کنید، بدون اینکه نیازی به تخصیص حافظه برای آنها در سطح فریمورک اندروید داشته باشید. لایه فریمورک اندروید نیز با روش های دسترسی ارائه شده که در کلاس های لایه بازتابی تولید می شوند به این متغیرها دسترسی دارد. اگر این متغیرها در لایه زمان اجرا RenderScript مقداردهی اولیه شوند، از آن مقادیر برای مقداردهی اولیه در لایه چارچوب Android استفاده می شود. اگر متغیرهای سراسری به‌عنوان const علامت‌گذاری شوند، متد set ایجاد نمی‌شود. برای جزئیات بیشتر اینجا را نگاه کنید.

توجه: اگر از ساختارهای RenderScript خاصی استفاده می کنید که حاوی اشاره گر هستند، مانند rs_program_fragment و rs_allocation ، باید ابتدا یک شی از کلاس فریمورک اندروید مربوطه را به دست آورید و سپس متد set را برای آن ساختار فراخوانی کنید تا حافظه به زمان اجرا RenderScript متصل شود. شما نمی توانید مستقیماً این ساختارها را در لایه زمان اجرا RenderScript دستکاری کنید. این محدودیت برای ساختارهای تعریف شده توسط کاربر که حاوی نشانگر هستند، اعمال نمی شود، زیرا در وهله اول نمی توان آنها را به یک کلاس لایه بازتابی صادر کرد. اگر بخواهید یک ساختار غیر ایستا و سراسری را که حاوی یک اشاره گر است، اعلام کنید، یک خطای کامپایلر ایجاد می شود.

RenderScript از اشاره گرها نیز پشتیبانی می کند، اما باید به صراحت حافظه را در کد فریمورک اندروید خود اختصاص دهید. وقتی یک اشاره گر سراسری را در فایل .rs خود اعلام می کنید، حافظه را از طریق کلاس لایه بازتاب شده مناسب تخصیص می دهید و آن حافظه را به لایه RenderScript اصلی متصل می کنید. شما می توانید با این حافظه از لایه فریمورک اندروید و همچنین لایه RenderScript تعامل داشته باشید که به شما انعطاف پذیری برای تغییر متغیرها در مناسب ترین لایه را ارائه می دهد.

تخصیص و اتصال حافظه پویا به RenderScript

برای تخصیص حافظه پویا، باید سازنده یک کلاس Script.FieldBase را فراخوانی کنید، که رایج ترین راه است. یک جایگزین ایجاد یک Allocation به صورت دستی است که برای مواردی مانند نشانگرهای نوع اولیه مورد نیاز است. برای سادگی هر زمان که در دسترس است باید از سازنده کلاس Script.FieldBase استفاده کنید. پس از به دست آوردن تخصیص حافظه، متد refled bind اشاره گر را فراخوانی کنید تا حافظه اختصاص داده شده به زمان اجرا RenderScript متصل شود.

مثال زیر حافظه را هم برای یک اشاره گر نوع اولیه، intPointer و یک اشاره گر به یک ساختار، touchPoints اختصاص می دهد. همچنین حافظه را به RenderScript متصل می کند:

کاتلین

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 و هم در لایه فریم ورک اندروید بخوانید و بنویسید.

حافظه تخصیص یافته استاتیک با محدودیت ارتباطی یک طرفه در سطح زمان اجرا RenderScript همراه است. وقتی کد RenderScript مقدار یک متغیر را تغییر می‌دهد، برای اهداف کارآمدی به لایه چارچوب Android بازگردانده نمی‌شود. آخرین مقداری که از فریم ورک اندروید تنظیم می‌شود، همیشه در حین فراخوانی متد get بازگردانده می‌شود. با این حال، زمانی که کد فریمورک اندروید یک متغیر را تغییر می‌دهد، این تغییر می‌تواند به صورت خودکار به زمان اجرا RenderScript منتقل شود یا در زمان دیگری همگام‌سازی شود. اگر نیاز به ارسال داده از زمان اجرا RenderScript به لایه فریمورک اندروید دارید، می توانید از تابع rsSendToClient() برای غلبه بر این محدودیت استفاده کنید.

هنگامی که با حافظه تخصیص یافته به صورت پویا کار می کنید، اگر تخصیص حافظه را با استفاده از اشاره گر مربوط به آن تغییر دهید، هر تغییری در لایه زمان اجرا RenderScript به لایه چارچوب Android منتشر می شود. تغییر یک شی در لایه فریمورک اندروید بلافاصله منتشر می شود که به لایه زمان اجرا RenderScript برمی گردد.

خواندن و نوشتن برای متغیرهای سراسری

خواندن و نوشتن برای متغیرهای سراسری یک فرآیند ساده است. می توانید از متدهای دسترسی در سطح چارچوب اندروید استفاده کنید یا آنها را مستقیماً در کد 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;

شما می توانید مقادیری را به ساختار در لایه فریمورک اندروید به این صورت اختصاص دهید. این مقادیر به صورت ناهمزمان به سطح زمان اجرا RenderScript منتشر می شوند:

کاتلین

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

با کد زیر می توانید مقادیر موجود در لایه فریمورک اندروید را بخوانید. به خاطر داشته باشید که این کد فقط زمانی مقداری را برمی‌گرداند که در سطح فریمورک اندروید تنظیم شده باشد. اگر مقدار را فقط در سطح زمان اجرا RenderScript تنظیم کنید، یک استثنای اشاره گر تهی دریافت خواهید کرد:

کاتلین

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

خواندن و نوشتن اشاره گرهای جهانی

با فرض اینکه حافظه در سطح فریمورک اندروید تخصیص یافته و به زمان اجرا RenderScript محدود شده است، می توانید با استفاده از متدهای get و set برای آن اشاره گر، حافظه را از سطح فریمورک اندروید بخوانید و بنویسید. در لایه زمان اجرا RenderScript، می توانید به طور معمول با اشاره گرها در حافظه بخوانید و بنویسید و برخلاف حافظه تخصیص یافته استاتیک، تغییرات به لایه فریمورک اندروید بازگردانده می شوند.

برای مثال، نشانگر زیر را به struct در فایلی به نام rsfile.rs می دهیم:

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

Point_t *point;

با فرض اینکه قبلاً حافظه را در لایه چارچوب Android تخصیص داده اید، می توانید به مقادیر موجود در struct به طور معمول دسترسی داشته باشید. هر تغییری که از طریق متغیر اشاره گر آن در ساختار ایجاد می کنید به طور خودکار در لایه فریمورک اندروید در دسترس است:

کاتلین

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

جاوا

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

همچنین می توانید مقادیر را روی اشاره گر در لایه فریمورک اندروید بخوانید و بنویسید:

کاتلین

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 مجدداً متصل کنید.