النص البرمجي RenderScript المتقدّم

وبما أنّ التطبيقات التي تستخدم RenderScript لا تزال تعمل داخل جهاز Android افتراضي، يمكنك الوصول إلى جميع واجهات برمجة التطبيقات لإطار العمل التي تعرفها، ولكن يمكنك واستخدام RenderScript عندما يكون ذلك مناسبًا. لتسهيل هذا التفاعل بين إطار العمل ووقت تشغيل RenderScript، فإن هناك طبقة متوسطة من التعليمات البرمجية لتقديمها لتسهيل الاتصال وإدارة الذاكرة بين مستويي التعليمات البرمجية. تتناول هذه الوثيقة مزيدًا من التفاصيل حول هذه طبقات الرموز المختلفة وكذلك كيفية مشاركة الذاكرة بين جهاز Android افتراضي وقت تشغيل RenderScript.

طبقة وقت تشغيل RenderScript

يتم تجميع رمز RenderScript في طبقة وقت تشغيل مضغوطة ومحددة بشكل جيد. تتيح واجهات برمجة التطبيقات لوقت تشغيل RenderScript دعمًا عملية حسابية مكثفة يمكن حملها وقابلة للتطوير تلقائيًا عدد النوى المتوفرة في أي معالج.

ملاحظة: يجب أن تكون دوال C العادية في NDK. بضمان تشغيله على وحدة معالجة مركزية (CPU)، ولذلك لا يمكن لـ RenderScript الوصول إلى هذه المكتبات، لأنّ RenderScript مصمم للعمل على أنواع مختلفة من معالِجات البيانات.

أنت تحدد رمز RenderScript في .rs. و.rsh ملف في الدليل src/ من مشروع Android. الرمز إلى رمز بايت متوسط باستخدام المحول البرمجي لـ llvm الذي يتم تشغيله كجزء من أحد إصدارات Android. عند تقديم طلبك يعمل على أحد الأجهزة، يتم تجميع رمز البايت (في الوقت المناسب) على رمز الجهاز بواسطة المحول البرمجي لـ llvm المتوفّر على الجهاز وقد تم تحسين الرمز البرمجي للجهاز الجهاز وذاكرة التخزين المؤقت أيضًا، لذا فإن الاستخدامات اللاحقة للتطبيق المفعَّل لـ RenderScript لا إعادة تحويل رمز البايت.

تشمل بعض الميزات الرئيسية لمكتبات وقت تشغيل RenderScript ما يلي:

  • ميزات طلب تخصيص الذاكرة
  • يشير هذا المصطلح إلى مجموعة كبيرة من الدوال الحسابية التي تتضمّن نُسخًا محمّلة بشكل زائد عن طريق إدخال قيم عددية وموجَّهة. من العديد من الإجراءات الروتينية الشائعة. عمليات مثل الجمع والضرب والضرب النقطي ومعامل الضرب المتقاطع بالإضافة إلى دوال الحساب الذرّي والمقارنة.
  • إجراءات التحويل لأنواع البيانات الأساسية والمتجهات، وإجراءات المصفوفة، والتاريخ والوقت سلاسل الإجراءات
  • أنواع البيانات وهياكلها لدعم نظام RenderScript، مثل أنواع المتجهات تحديد متجهين أو ثلاثة أو أربعة متجهات.
  • وظائف التسجيل

يمكنك الاطّلاع على مرجع واجهة برمجة التطبيقات لوقت تشغيل RenderScript للحصول على مزيد من المعلومات حول الدوال المتاحة.

الطبقة المنعكسة

الطبقة المنعكسة هي مجموعة من الفئات التي تنشئها أدوات إنشاء Android للسماح بالوصول إلى وقت تشغيل RenderScript من إطار عمل Android. توفر هذه الطبقة أيضًا طرقًا والتركيبات الإنشائية التي تسمح لك بتخصيص ذاكرة للمؤشرات التي يتم تحديدها في والتعامل معها رمز RenderScript. تصف القائمة التالية الفوائد المكونات التي تنعكس:

  • يتم إنشاء كل ملف .rs تنشئه إلى صف باسم project_root/gen/package/name/ScriptC_renderscript_filename من إجمالي النوع ScriptC. هذا الملف هو الإصدار .java من .rs، الذي يمكنك استدعاؤه من إطار عمل Android. تحتوي هذه الفئة على العناصر التالية من ملف .rs:
    • الدوال غير الثابتة
    • متغيّرات RenderScript غير الثابتة والعمومية. إكسسوار لكل متغير، بحيث يمكنك قراءة اكتب متغيرات 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;
}

فسيتم إنشاء رمز 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، تحديد مساحة الذاكرة المخصّصة لعملية تخصيص هذه الذاكرة. توجد أربع مساحات في الذاكرة الاحتمالات:
    • USAGE_SCRIPT: تخصيص في ذاكرة النص البرمجي مساحة. وهذه هي مساحة الذاكرة التلقائية إذا لم تحدِّد مساحة ذاكرة.
    • USAGE_GRAPHICS_TEXTURE: يُخصّص في مساحة الذاكرة في وحدة معالجة الرسومات.
    • USAGE_GRAPHICS_VERTEX: يتم تخصيص هذا الجزء في رأس الصفحة. مساحة ذاكرة وحدة معالجة الرسومات.
    • USAGE_GRAPHICS_CONSTANTS: يُخصّص في مساحة ذاكرة ثابتة لوحدة معالجة الرسومات التي تستخدمها كائنات البرنامج المختلفة.

    يمكنك تحديد مساحات ذاكرة متعددة باستخدام عامل التشغيل 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، على شكل كائن. تكون هذه الفئة المتداخلة مفيدة إذا كان من المنطقي العمل بشكل أفضل مع struct في رمز Android. عندما تنتهي من معالجة الكائن، يمكنك نقل العنصر إلى الذاكرة المخصَّصة من خلال طلب الرمز set(Item i, int index, boolean copyNow) وضبط Item على الموضع المطلوب. الصفيفة. يمكن لبيئة تشغيل RenderScript تلقائيًا الوصول إلى الذاكرة المكتوبة حديثًا.
  • طرق الموصّل للحصول على قيم كل حقل وتعيينها في بنية. كل من هذه تحتوي طرق الموصّل على المعلمة index لتحديد struct في الصفيف الذي تريد القراءة أو الكتابة فيه. لكل طريقة setter أيضًا قيمة مَعلمة copyNow التي تحدّد ما إذا كانت ستتم مزامنة هذه الذاكرة على الفور أم لا إلى وقت تشغيل RenderScript. لمزامنة أي ذكرى لم تتم مزامنتها، يُرجى الاتصال copyAll()
  • تنشئ الطريقة createElement() وصفًا للبنية في الذاكرة. هذا النمط يستخدم الوصف لتخصيص ذاكرة تتكون من عنصر واحد أو عدة عناصر.
  • يعمل resize() إلى حدّ كبير مثل realloc() في C، ما يسمح لك بما يلي: توسيع الذاكرة المخصّصة سابقًا، مع الحفاظ على القيم الحالية التي كانت مضبوطة في السابق إنشاء.
  • يعمل 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 الافتراضي إلى وقت تشغيل RenderScript (لا يمكنك تخصيص الذاكرة في ملف .rs). لمزيد من المعلومات، يُرجى الاطّلاع على مقالة العمل باستخدام الذاكرة المخصّصة

واجهات برمجة التطبيقات لتخصيص الذاكرة

ستظل التطبيقات التي تستخدم RenderScript تعمل في جهاز Android افتراضي. في المقابل، يتم تشغيل رمز RenderScript في الأصل يحتاج إلى إذن بالوصول إلى الذاكرة المخصّصة في جهاز Android الافتراضي. لتحقيق ذلك، يجب عليك يتم إرفاق الذاكرة المخصّصة في الجهاز الافتراضي ببيئة تشغيل RenderScript. هذا النمط اسمها الربط، يسمح لوقت تشغيل RenderScript بالعمل بسلاسة مع الذاكرة التي ولكن لا يمكن تخصيصها بشكل صريح. والنتيجة النهائية هي في الأساس هي نفسها كما لو كان لديك تسمى malloc في C. أما الميزة الإضافية فهي أن الجهاز الافتراضي الذي يعمل بنظام التشغيل Android يمكنه تنفيذ جمع البيانات المهملة وكذلك مشاركة الذاكرة مع طبقة وقت تشغيل RenderScript. الربط ضروري فقط للذاكرة المخصصة ديناميكيًا. ثابت يتم تلقائيًا إنشاء ذاكرة مخصّصة لرمز RenderScript في وقت التجميع. راجِع الشكل 1. لمزيد من المعلومات حول كيفية تخصيص الذاكرة.

لإتاحة نظام تخصيص الذاكرة هذا، تتوفّر مجموعة من واجهات برمجة التطبيقات التي تتيح للجهاز الافتراضي الذي يعمل بنظام التشغيل Android تنفيذ ما يلي: تخصيص ذاكرة وتقديم وظائف مشابهة لاستدعاء malloc. هذه الصفوف بشكل أساسي كيفية تخصيص الذاكرة وتنفيذ التخصيص. للأفضل فهم كيفية عمل هذه الفئات، يكون من المفيد التفكير فيها فيما يتعلق بتجربة مكالمة malloc التي يمكن أن تبدو على النحو التالي:

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

يمكن تقسيم المكالمة malloc إلى جزأين: حجم الذاكرة المخصّصة (sizeof(int)) إلى جانب عدد وحدات هذه الذاكرة التي يجب تخصيصها (10). يوفر إطار عمل Android فئات لهذين الجزأين بالإضافة إلى فئة لتمثيل malloc نفسها.

تمثّل الفئة Element الجزء (sizeof(int)). في طلب malloc وهي تتضمن خلية واحدة من عملية تخصيص للذاكرة، مثل خلية واحدة قيمة عائمة أو هيكلة. تضم الفئة Type العنصر Element. وعدد العناصر المراد تخصيصها (10 في المثال). يمكنك اعتبار Type على أنه مصفوفة من Element. تقوم الفئة Allocation بالأداء الفعلي يتم تخصيص الذاكرة استنادًا إلى قيمة Type محدَّدة ويمثّل ذلك الذاكرة المخصّصة الفعلية.

في معظم الحالات، لن تحتاج إلى استدعاء واجهات برمجة التطبيقات المخصصة لتخصيص الذاكرة مباشرةً. الطبقة المنعكسة تنشئ الفئات رمزًا لاستخدام واجهات برمجة التطبيقات هذه تلقائيًا وكل ما عليك فعله لتخصيص الذاكرة هو استدعاء الدالة الإنشائية التي تم تعريفها في إحدى فئات الطبقة المنعكسة ثم تربط الذاكرة الناتجة Allocation إلى RenderScript. هناك بعض المواقف التي قد تحتاج فيها إلى استخدام هذه الصفوف بشكل مباشر لتخصيص ذاكرة على مثل تحميل صورة نقطية من مورد أو عندما تريد تخصيص ذاكرة للمؤشرات الأولية. يمكنك الاطّلاع على كيفية إجراء ذلك في تخصيص ذاكرة وربطها بقسم RenderScript يوضّح الجدول التالي فئات إدارة الذاكرة الثلاث بمزيد من التفصيل:

نوع كائن Android الوصف
Element

يصف العنصر خلية واحدة من تخصيص الذاكرة ويمكن أن يكون له شكلان: أساسي أو للغاية.

يحتوي العنصر الأساسي على مكوّن واحد من بيانات أي نوع بيانات صالح في RenderScript. تتضمن أمثلة أنواع بيانات العناصر الأساسية قيمة float مفردة أو متجه float4 أو لون RGB-565 واحد.

تحتوي العناصر المعقدة على قائمة من العناصر الأساسية ويتم إنشاؤها من struct التي تذكرها في رمز RenderScript. على سبيل المثال، تخصيص الوقت يمكن أن يحتوي على عدة عناصر struct مرتبة في الذاكرة. يُعد كل هيكل بمثابة بدلاً من كل نوع بيانات داخل هذه البنية.

Type

النوع هو نموذج لتخصيص الذاكرة ويتألف من عنصر وعنصر واحد أو أكثر. الأبعاد. يصف هذا الملف تنسيق الذاكرة (مصفوفة من Element ثانية)، ولكنّه لا يخصِّص الذاكرة للبيانات التي تستخدمها. يصفها.

يتكون النوع من خمسة أبعاد: X، Y، Z، LOD (مستوى التفاصيل)، والوجوه (للمكعب) الخريطة). يمكنك ضبط الأبعاد (س) و(ص) و(ع) على أي قيمة عدد صحيح موجب داخل القيود المفروضة على الذاكرة المتاحة. إنّ عملية تخصيص البُعد الواحد تتضمّن البُعد "س" أكبر من صفر في حين أن البُعدين "ص" و"ع" هما صفر للإشارة إلى عدم توفّرهما. بالنسبة على سبيل المثال، يعد تخصيص x=10، وy=1 ثنائي الأبعاد وx=10، وy=0 تُعد ذات بُعد واحد. يعد بُعدا LOD وFace قيمتين منطقيتين للإشارة إلى وجود أو غير موجود.

Allocation

يوفر التخصيص الذاكرة للتطبيقات بناءً على وصف الذاكرة التي يتم تمثيلها بواسطة Type. يمكن أن تتوفّر الذاكرة المخصّصة في العديد من مساحات الذاكرة بشكل متزامن. إذا تم تعديل الذاكرة في مساحة واحدة، يجب عليك بشكل صريح مزامنة الذاكرة، ليتم تعديلها في جميع المساحات الأخرى التي توجد فيها

يتم تحميل بيانات التخصيص بإحدى الطريقتين الأساسيتين: النوع الذي تم تحديده والنوع غير المحدّد. بالنسبة إلى الصفائف البسيطة، هناك دوال copyFrom() تأخذ صفيفًا من نظام Android ونسخه إلى مخزن ذاكرة الطبقة الأصلية. تتيح المتغيرات غير محددة نظام Android بنسخ صفائف الهياكل لأنها لا تتيح والهياكل. على سبيل المثال، إذا كان هناك تخصيص يمثل صفيفًا من عدد عائم، فإن البيانات يمكن نسخ البيانات المضمَّنة في مصفوفة float[n] أو المصفوفة byte[n*4].

استخدام الذاكرة

في وقت التجميع، يتم تخصيص ذاكرة مخصّصة للمتغيّرات العمومية غير الثابتة التي تشير إليها في RenderScript. يمكنك التعامل مع هذه المتغيرات مباشرةً في رمز RenderScript بدون الحاجة إلى تخصيص الذاكرة على مستوى إطار عمل Android. يمكن لطبقة إطار عمل Android أيضًا الوصول إلى هذه المتغيّرات مع طرق الموصّل المتوفرة التي تم إنشاؤها في فئات الطبقات المنعكسة. إذا كانت هذه المتغيرات في طبقة وقت تشغيل RenderScript، تُستخدم هذه القيم لتهيئة بيئة تشغيل RenderScript والقيم في طبقة إطار عمل Android. إذا تم وضع علامة على المتغيّرات العمومية على أنّها ثابتة، يتم عندها استخدام طريقة set لم يتم إنشاؤها. لمزيد من التفاصيل، يمكنك الاطّلاع هنا.

ملاحظة: إذا كنت تستخدم بُنى RenderScript معيّنة تحتوي على مؤشرات، مثل rs_program_fragment وrs_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 لأغراض الكفاءة. القيمة الأخيرة يتم دائمًا إرجاعه من إطار عمل 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
}

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، يمكنك قراءة الذاكرة وكتابتها من مستوى إطار عمل Android باستخدام الطريقتَين get وset لهذا المؤشر. في طبقة وقت تشغيل RenderScript، يمكنك القراءة والكتابة في الذاكرة باستخدام المؤشرات كالمعتاد ويتم نشر التغييرات الرجوع إلى طبقة إطار عمل Android، على عكس الذاكرة المخصّصة بشكل ثابت.

على سبيل المثال، باستخدام المؤشر التالي لـ struct في ملف اسمه rsfile.rs:

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. كل مرة يتم فيها إجراء تغيير على قيمة.