RenderScript ขั้นสูง

เนื่องจากแอปพลิเคชันที่ใช้ RenderScript ยังคงทำงานภายใน Android VM คุณจะมีสิทธิ์เข้าถึง API ของเฟรมเวิร์กทั้งหมดที่คุณคุ้นเคย แต่สามารถ ใช้ RenderScript ตามความเหมาะสม เพื่ออำนวยความสะดวกในการโต้ตอบระหว่าง เฟรมเวิร์กและรันไทม์ RenderScript ขึ้น ชั้นโค้ดระดับกลางยัง แสดงเพื่ออำนวยความสะดวกในการสื่อสารและการจัดการหน่วยความจำระหว่างโค้ดทั้ง 2 ระดับ เอกสารนี้ลงรายละเอียดเกี่ยวกับ เลเยอร์ต่างๆ ของโค้ดได้ รวมทั้งวิธีการแชร์หน่วยความจำระหว่าง Android VM และ รันไทม์ของ RenderScript

เลเยอร์รันไทม์ของ RenderScript

โค้ด RenderScript ได้รับการคอมไพล์และ ดำเนินการในเลเยอร์รันไทม์ที่กะทัดรัดและกำหนดไว้เป็นอย่างดี API รันไทม์ของ RenderScript รองรับ การคำนวณอย่างหนักที่พกพาได้และปรับขนาดได้โดยอัตโนมัติ จำนวนแกนที่มีอยู่บนโปรเซสเซอร์

หมายเหตุ: ฟังก์ชัน C มาตรฐานใน NDK ต้องมีลักษณะดังนี้ รับประกันการแสดงผลบน CPU ดังนั้น RenderScript จึงไม่สามารถเข้าถึงไลบรารีเหล่านี้ เนื่องจาก RenderScript ได้รับการออกแบบมาให้ทำงานกับโปรเซสเซอร์ประเภทต่างๆ

คุณกำหนดโค้ด RenderScript ใน .rs และ .rsh ไฟล์ในไดเรกทอรี src/ ของโปรเจ็กต์ Android ของคุณ โค้ด จะถูกคอมไพล์เป็นไบต์โค้ดกลางโดย คอมไพเลอร์ llvm ที่ทำงานเป็นส่วนหนึ่งของบิลด์ Android กรณีที่แอปพลิเคชันของคุณ ทำงานบนอุปกรณ์ จากนั้นจะมีการรวบรวมไบต์โค้ด (แบบทันท่วงที) ไปยังโค้ดของเครื่องโดยบุคคลอื่น คอมไพเลอร์ llvm ที่อยู่ในอุปกรณ์ โค้ดของเครื่องได้รับการเพิ่มประสิทธิภาพเพื่อ อุปกรณ์และแคชด้วย ดังนั้นการใช้งานแอปพลิเคชันที่เปิดใช้งาน RenderScript ในภายหลังจึงไม่ คอมไพล์ไบต์โค้ดอีกครั้ง

คุณลักษณะสำคัญบางประการของไลบรารีรันไทม์ RenderScript มีดังนี้

  • ฟีเจอร์คำขอจัดสรรหน่วยความจำ
  • คอลเล็กชันฟังก์ชันคณิตศาสตร์ขนาดใหญ่ ซึ่งมีทั้งเวอร์ชันสเกลาร์และเวอร์ชันที่มีการพิมพ์เวกเตอร์มากเกินไป ของกิจวัตรประจำวันต่างๆ การดำเนินการ เช่น การบวก การคูณ ผลคูณไขว้ และข้ามผลิตภัณฑ์ รวมถึงฟังก์ชันเลขคณิตและการเปรียบเทียบ
  • กิจวัตร Conversion ของประเภทข้อมูลและเวกเตอร์พื้นฐาน กิจวัตรเมทริกซ์ รวมถึงวันที่และเวลา กิจวัตร
  • ประเภทข้อมูลและโครงสร้างที่จะรองรับระบบ RenderScript เช่น ประเภทเวกเตอร์สำหรับ โดยกำหนด 2, 3 หรือ 4 เวกเตอร์
  • ฟังก์ชันการบันทึก

ดูข้อมูลอ้างอิงของ API รันไทม์ใน 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 ซึ่งให้คุณจัดสรรหน่วยความจำสำหรับอินสแตนซ์นี้อย่างน้อย 1 รายการ 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 */);
    }
}

จะมอบโค้ดที่สร้างขึ้นให้คุณเพื่อความสะดวกในการจัดสรรหน่วยความจำสำหรับ Struct ที่ขอ ผ่านรันไทม์ RenderScript และโต้ตอบกับ struct ในหน่วยความจำ คลาสของ struct แต่ละรายการจะกำหนดเมธอดและตัวสร้างต่อไปนี้

  • เครื่องมือสร้างที่มีการโหลดมากเกินไปซึ่งช่วยให้คุณจัดสรรหน่วยความจำได้ เครื่องมือสร้าง ScriptField_struct_name(RenderScript rs, int count) อนุญาตให้ คุณสามารถกำหนดจำนวนโครงสร้างที่ต้องการจัดสรรหน่วยความจำด้วย พารามิเตอร์ count ตัวสร้าง ScriptField_struct_name(RenderScript rs, int count, int usages) กำหนดพารามิเตอร์เพิ่มเติม usages ซึ่ง จะช่วยให้คุณระบุพื้นที่หน่วยความจำของการจัดสรรหน่วยความจำนี้ได้ มีพื้นที่หน่วยความจำ 4 รูปแบบ ที่เป็นไปได้:
    • USAGE_SCRIPT: จัดสรรในหน่วยความจำของสคริปต์ พื้นที่ทำงาน นี่คือพื้นที่หน่วยความจำเริ่มต้นหากคุณไม่ได้ระบุพื้นที่หน่วยความจำ
    • USAGE_GRAPHICS_TEXTURE: จัดสรรใน พื้นที่หน่วยความจำพื้นผิวของ GPU
    • USAGE_GRAPHICS_VERTEX: จัดสรรในส่วนยอดมุม พื้นที่หน่วยความจำของ GPU
    • USAGE_GRAPHICS_CONSTANTS: จัดสรรใน พื้นที่หน่วยความจำของ GPU คงที่ที่ออบเจ็กต์โปรแกรมต่างๆ ใช้

    คุณสามารถระบุพื้นที่หน่วยความจำได้หลายพื้นที่โดยใช้โอเปอเรเตอร์ OR แบบบิตไวส์ ดังนั้น แจ้งรันไทม์ RenderScript ที่คุณต้องการเข้าถึงข้อมูลใน พื้นที่หน่วยความจำที่ระบุ ตัวอย่างต่อไปนี้จัดสรรหน่วยความจำสำหรับประเภทข้อมูลที่กำหนดเอง ทั้งในพื้นที่หน่วยความจำของสคริปต์และ Vertex:

    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() จะสร้างคําอธิบายโครงสร้างในหน่วยความจำ ช่วงเวลานี้ คำอธิบาย ใช้สำหรับจัดสรรหน่วยความจำที่ประกอบด้วยองค์ประกอบอย่างน้อย 1 อย่าง
  • resize() ทำงานคล้ายกับ realloc() ในภาษา C ซึ่งช่วยให้คุณ ขยายหน่วยความจำที่จัดสรรไว้ก่อนหน้านี้ โดยรักษาค่าปัจจุบันที่ก่อนหน้านี้ใช้ สร้าง แล้ว
  • copyAll() ซิงค์ข้อมูลหน่วยความจำที่ตั้งค่าไว้ในระดับเฟรมเวิร์กกับ รันไทม์ของ RenderScript เมื่อคุณเรียกใช้ Method ของ Set Accessor ในสมาชิกจะมีตัวเลือกเสริม พารามิเตอร์บูลีน copyNow ที่คุณระบุได้ การระบุ true จะซิงค์หน่วยความจำเมื่อคุณเรียกใช้เมธอด หากคุณระบุ "เท็จ" คุณสามารถโทรหา 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 ทำงานกับหน่วยความจำที่ คำขอแต่ไม่สามารถจัดสรรไว้อย่างชัดเจนได้ โดยพื้นฐานแล้ว ผลลัพธ์จะเหมือนกับว่าคุณมี ที่เรียกว่า malloc ใน C ประโยชน์เพิ่มเติมคือ Android VM สามารถจัดเก็บขยะและ แชร์หน่วยความจำกับเลเยอร์รันไทม์ของ RenderScript จำเป็นต้องเชื่อมโยงเฉพาะหน่วยความจำที่จัดสรรแบบไดนามิกเท่านั้น แบบคงที่ ระบบจะสร้างหน่วยความจำที่จัดสรรสำหรับโค้ด RenderScript โดยอัตโนมัติในเวลาคอมไพล์ ดูรูปที่ 1 เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับการจัดสรรหน่วยความจำ

มีชุดของ API ที่อนุญาตให้ Android VM สามารถรองรับระบบการจัดสรรหน่วยความจำนี้ได้ จัดสรรหน่วยความจำและมีฟังก์ชันการทำงานที่คล้ายกับการโทร malloc ชั้นเรียนเหล่านี้ อธิบายถึงวิธีจัดสรรหน่วยความจำเป็นหลัก รวมถึงจัดสรรหน่วยความจำด้วย ให้ดียิ่งขึ้น เข้าใจวิธีการทำงานของชั้นเรียนเหล่านี้ การคิดถึงชั้นเรียนเกี่ยวกับ การเรียก malloc ที่อาจมีลักษณะดังนี้

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

การเรียกใช้ malloc สามารถแบ่งออกเป็น 2 ส่วน ได้แก่ ขนาดของหน่วยความจำที่จัดสรร (sizeof(int)) รวมถึงจำนวนหน่วยของหน่วยความจำนั้นที่ควรจัดสรร (10) เฟรมเวิร์ก Android จะสอนทั้ง 2 ส่วนนี้ในลักษณะต่อไปนี้ ชั้นเรียนแสดงถึง malloc เอง

คลาส Element แสดงถึงส่วน (sizeof(int)) ของการเรียก malloc และสรุปการจัดสรรหน่วยความจำ 1 เซลล์เช่นเซลล์เดียว จำนวนลอยตัวหรือโครงสร้าง คลาส Type สรุป Element และจำนวนองค์ประกอบที่จะจัดสรร (10 ในตัวอย่างของเรา) ให้ Type เป็น อาร์เรย์ของ Element ชั้นเรียน Allocation จะดำเนินการตาม การจัดสรรหน่วยความจำตาม Type ที่ระบุและแสดงถึงหน่วยความจำที่จัดสรรตามจริง

ในกรณีส่วนใหญ่ คุณไม่จำเป็นต้องเรียกใช้ API การจัดสรรหน่วยความจำเหล่านี้โดยตรง ชั้นที่สะท้อน คลาสจะสร้างโค้ดเพื่อใช้ API เหล่านี้โดยอัตโนมัติ และสิ่งที่คุณต้องทำเพื่อจัดสรรหน่วยความจำคือการเรียก เครื่องมือสร้างที่ประกาศไว้ในคลาสเลเยอร์หนึ่งที่สะท้อนแล้วจึงเชื่อมโยง หน่วยความจำผลลัพธ์ Allocation ไปยัง RenderScript อาจมีบางสถานการณ์ที่คุณอาจต้องใช้ชั้นเรียนเหล่านี้เพื่อจัดสรรหน่วยความจำใน เช่น การโหลดบิตแมปจากทรัพยากร หรือเมื่อคุณต้องการจัดสรรหน่วยความจำสำหรับตัวชี้ไปยัง ประเภทพื้นฐาน คุณสามารถดูวิธีดำเนินการได้ใน จัดสรรและเชื่อมโยงหน่วยความจำกับส่วน RenderScript ตารางต่อไปนี้จะอธิบายคลาสการจัดการหน่วยความจำ 3 คลาสโดยละเอียดยิ่งขึ้น

ประเภทออบเจ็กต์ Android คำอธิบาย
Element

องค์ประกอบจะอธิบายถึงการจัดสรรหน่วยความจำ 1 เซลล์และมี 2 รูปแบบ ได้แก่ แบบพื้นฐานหรือแบบพื้นฐาน ที่ซับซ้อน

องค์ประกอบพื้นฐานประกอบด้วยข้อมูลคอมโพเนนต์เดียวของประเภทข้อมูล RenderScript ที่ถูกต้อง ตัวอย่างประเภทข้อมูลองค์ประกอบพื้นฐาน ได้แก่ ค่า float ค่าเดียว, เวกเตอร์ float4 หรือ สี RGB-565 สีเดียว

องค์ประกอบที่ซับซ้อนประกอบด้วยรายการองค์ประกอบพื้นฐานและสร้างขึ้นจาก struct ที่คุณประกาศในโค้ด RenderScript เช่น การจัดสรร สามารถมี struct ได้หลายรายการที่จัดเรียงตามลำดับในหน่วยความจำ แต่ละโครงสร้างจะถือเป็น เอลิเมนต์ของตัวเอง ไม่ใช่ข้อมูลแต่ละประเภทภายในโครงสร้างนั้น

Type

ประเภทคือเทมเพลตการจัดสรรหน่วยความจำและประกอบด้วยองค์ประกอบ 1 รายการและอย่างน้อย 1 รายการ ขนาดกะทัดรัด โดยจะอธิบายเลย์เอาต์ของหน่วยความจำ (โดยพื้นฐานจะเป็นอาร์เรย์ของ Element) แต่ไม่ได้จัดสรรหน่วยความจำให้กับข้อมูลที่อยู่ในหน่วยความจำ อธิบาย

ประเภทประกอบด้วยมิติข้อมูล 5 ส่วน ได้แก่ X, Y, Z, LOD (ระดับรายละเอียด) และด้าน (ของลูกบาศก์ แผนที่) คุณสามารถตั้งค่ามิติข้อมูล X,Y,Z เป็นค่าจำนวนเต็มบวกภายในค่า หน่วยความจำที่ใช้ได้ การจัดสรรมิติข้อมูลเดียวมีมิติข้อมูล X เป็น มากกว่า 0 ขณะที่มิติข้อมูล Y และ Z เป็น 0 แสดงว่าไม่มีอยู่ สำหรับ ตัวอย่างเช่น การจัดสรร x=10, y=1 จะถือว่าเป็น 2 มิติ และ x=10, y=0 นั้น ถือว่าเป็นหนึ่งมิติ มิติข้อมูล LOD และใบหน้าเป็นบูลีนเพื่อระบุปัจจุบัน หรือว่าไม่มี

Allocation

การจัดสรรที่จะมอบหน่วยความจำสำหรับแอปพลิเคชันตามคำอธิบายของหน่วยความจำ ซึ่งแสดงด้วย Type หน่วยความจำที่จัดสรรจะอยู่ใน หลายพื้นที่หน่วยความจำพร้อมกัน ถ้าหน่วยความจำถูกแก้ไขในหนึ่งพื้นที่ คุณจะต้อง ซิงค์หน่วยความจำเพื่ออัปเดตในพื้นที่ว่างอื่นๆ ทั้งหมดที่มีหน่วยความจำนั้นอยู่

ระบบจะอัปโหลดข้อมูลการจัดสรรด้วยวิธีใดวิธีหนึ่งใน 2 วิธีหลัก ได้แก่ การเลือกประเภทและยกเลิกการเลือกประเภท สำหรับอาร์เรย์แบบง่ายจะมีฟังก์ชัน copyFrom() ที่นำอาร์เรย์จาก ระบบ Android แล้วคัดลอกไปยังหน่วยความจำเลเยอร์ดั้งเดิม ตัวแปรที่ไม่ได้เลือกช่วยให้ ระบบ Android สามารถคัดลอกอาร์เรย์ของโครงสร้างได้เพราะไม่รองรับ เหล่านี้ ตัวอย่างเช่น หากมีการจัดสรรที่เป็นอาร์เรย์ของ n ลอย ข้อมูล ที่อยู่ในอาร์เรย์ Float[n] หรืออาร์เรย์ byte[n*4] ได้

การทำงานกับหน่วยความจำ

ตัวแปรร่วมที่ไม่ใช่แบบคงที่ที่คุณประกาศใน RenderScript ได้รับการจัดสรรหน่วยความจำในเวลาคอมไพล์ คุณสามารถทำงานกับตัวแปรเหล่านี้ได้โดยตรงในโค้ด RenderScript โดยไม่ต้องจัดสรร ให้สมาชิกในระดับเฟรมเวิร์ก Android เลเยอร์เฟรมเวิร์ก Android มีสิทธิ์เข้าถึงตัวแปรเหล่านี้ด้วย ด้วยเมธอด Accessor ที่มีให้ซึ่งสร้างขึ้นในคลาสเลเยอร์สะท้อน หากตัวแปรเหล่านี้คือ ซึ่งเริ่มต้นไว้ที่เลเยอร์รันไทม์ RenderScript ค่าเหล่านี้จะใช้เพื่อเริ่มต้น ในเลเยอร์เฟรมเวิร์ก Android หากตัวแปรร่วมมีการทำเครื่องหมายเป็น Const ดังนั้นเมธอด 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;

คุณกำหนดค่าให้กับ Struct ในเลเยอร์เฟรมเวิร์ก 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 อีก ทุกครั้งที่คุณทำการเปลี่ยนแปลงกับค่า