מכיוון שאפליקציות שמשתמשות ב-RenderScript עדיין פועלות ב-VM של Android, יש לכם גישה לכל ממשקי ה-API של framework שאתם מכירים, אבל אתם יכולים להשתמש ב-RenderScript במקרים הרלוונטיים. כדי לאפשר את האינטראקציה הזו בין ה-framework וזמן הריצה של RenderScript, גם שכבת ביניים של קוד קיימים כדי להקל על התקשורת וניהול הזיכרון בין שתי רמות הקוד. במסמך הזה יש פירוט נוסף על הנושאים האלה בשכבות קוד שונות וגם האופן שבו הזיכרון משותף בין ה-VM של Android זמן ריצה של RenderScript.
שכבת זמן ריצה של RenderScript
קוד RenderScript שלך עבר הידור בשכבה קומפקטית ומוגדרת היטב של סביבת זמן ריצה. ממשקי ה-API של זמן הריצה של RenderScript מציעים תמיכה ב עיבוד נתונים אינטנסיבי שניתן לנייד וניתן להתאמה באופן אוטומטי את כמות הליבות הזמינות במעבד.
הערה: הפונקציות הסטנדרטיות של C ב-NDK חייבות להיות מובטח שהוא יפעל במעבד (CPU), כך ש-RenderScript לא יוכל לגשת לספריות האלה, כי RenderScript נועד לפעול בסוגים שונים של מעבדים.
הגדרת קוד RenderScript שלך ב-.rs
ו-.rsh
קבצים בספרייה src/
של פרויקט Android. הקוד
עובר לבייטקוד (bytecode) מתווך באמצעות
מהדר (compiler) llvm
שפועל כחלק מגרסת build של Android. כשהבקשה
פועל במכשיר, והבייטקוד עובר הידור (בדיוק בזמן) לקוד מכונה על ידי משתמש אחר
מהדר (compiler) llvm
שנמצא במכשיר. קוד המכונה עבר אופטימיזציה
במכשיר וגם במטמון, כך ששימושים עתידיים באפליקציה שהופעל בה RenderScript לא
להדר מחדש את הבייטקוד.
אלה כמה מהתכונות העיקריות של ספריות זמן הריצה של RenderScript:
- תכונות הבקשה להקצאת זיכרון
- אוסף גדול של פונקציות מתמטיות עם גרסאות עם עומס יתר בהקלדת וקטורים ובסקלריות מתרחישים נפוצים רבים. פעולות כמו הוספה, כפל, מכפלה ומוצר חוצות-מוצרים וגם פונקציות אריתמטיות והשוואה אטומיות.
- תרחישים של המרות עבור וקטורים וסוגי נתונים פרימיטיביים, תרחישים של מטריצות ותאריך ושעה תרחישים
- סוגי נתונים ומבנים שיתמכו במערכת RenderScript, כמו סוגי וקטורים הגדרה של וקטורים של שניים, שלושה או ארבעה.
- פונקציות רישום ביומן
מידע נוסף על הפונקציות הזמינות זמין בחומר העזר בנושא API של זמן ריצה של RenderScript.
שכבה מוחזרת
השכבה שמשתקפת היא קבוצה של מחלקות שכלי ה-build של Android יוצרים כדי לאפשר גישה לסביבת זמן הריצה של RenderScript מה-framework של Android. השכבה הזאת מספקת גם שיטות ובנאים שמאפשרים לכם להקצות ולעבוד עם זיכרון עבור מצביעים שמוגדרים בקוד RenderScript. הרשימה הבאה מתארת את הרכיבים שמשתקפים:
- כל קובץ
.rs
שיוצרים נוצר לכיתה בשםproject_root/gen/package/name/ScriptC_renderscript_filename
מתוך מקלידיםScriptC
. הקובץ הזה הוא גרסת.java
של את הקובץ.rs
, שאפשר לקרוא לו מה-framework של Android. הכיתה הזו מכילה את הפריטים הבאים שמשתקפים מקובץ.rs
:- פונקציות לא סטטיות
- משתנים גלובליים לא סטטיים של RenderScript. אביזר
לכל משתנה נוצרות שיטות, כך שאפשר לקרוא
לכתוב את המשתנים של RenderScript מ-Android
. אם משתנה גלובלי מאותחל
בשכבת זמן הריצה של RenderScript, הערכים האלה משמשים כדי
אתחול הערכים התואמים ב-framework של Android
בשכבת זרימת הנתונים. אם משתנים גלובליים מסומנים כ-
const
, אז ה-methodset
שנוצר. חיפוש כאן אפשר לקבל פרטים נוספים. - מצביעים גלובליים
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 מתוכננת אסינכרוני. כשקוד framework של Android שולח קריאה ל-RenderScript, הקריאה נמצאת בתור ו לבצע אותו כשאפשר. ההגבלה הזו מאפשרת למערכת RenderScript לפעול ללא קבוע את ההפרעה לו, ולהגביר את היעילות. אם לפונקציות הייתה הרשאה להחזיר ערכים, הקריאה תיחסם עד שהערך יוחזר.
אם רוצים שקוד RenderScript ישלח ערך חזרה ל-framework של 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
מגדירה את השיטות ואת הבנאים הבאים:
- עומס יתר במבנים שמאפשרים לכם להקצות זיכרון.
constructor של
ScriptField_struct_name(RenderScript rs, int count)
מאפשר כדי להגדיר את מספר המבנים שרוצים להקצות להם זיכרוןcount
. ה-constructor של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 )
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 יש גישה אוטומטית לזיכרון החדש שנכתב. - methods של רכיבי גישה כדי לקבל ולהגדיר את הערכים של כל שדה במבנה. כל אחד מאלה
ל-methods של רכיב הגישה יש פרמטר
index
שמציין אתstruct
המערך שרוצים לקרוא או לכתוב בו. לכל שיטת הגדרה יש גם פרמטרcopyNow
שמציין אם לסנכרן את הזיכרון הזה באופן מיידי לסביבת זמן הריצה של RenderScript. כדי לסנכרן זיכרון שלא סונכרן, צריך להתקשרcopyAll()
- השיטה
createElement()
יוצרת תיאור של המבנה בזיכרון. הזה משמש להקצאת זיכרון שמורכב מרכיב אחד או יותר. resize()
פועל כמוrealloc()
ב-C, ומאפשר להרחיב את הזיכרון שהוקצה בעבר, תוך שמירה על הערכים הנוכחיים נוצר.- הפונקציה
copyAll()
מסנכרנת את הזיכרון שהוגדר ברמת ה-framework עם זמן ריצה של RenderScript. כשמבצעים קריאה ל-method של רכיב גישה מוגדר אצל חבר, יש אפשרות פרמטר בוליאניcopyNow
שאפשר לציין. ציוןtrue
מסנכרן את הזיכרון כשקוראים ל-method. אם מציינים את הערך 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
מאפשרת לקשר את הזיכרון
שמוקצית ב-VM של Android לזמן הריצה של RenderScript (לא ניתן להקצות
בזיכרון שלך בקובץ .rs
). מידע נוסף זמין בעבודה
עם זיכרון מוקצה.
ממשקי API להקצאת זיכרון
אפליקציות שמשתמשות ב-RenderScript עדיין פועלות ב-VM של Android. עם זאת, קוד RenderScript בפועל רץ באופן טבעי
צריכה גישה לזיכרון שהוקצה ב-VM של Android. כדי לעשות את זה, צריך
לצרף את הזיכרון שהוקצה ל-VM לסביבת זמן הריצה של RenderScript. הזה
שנקרא 'קישור', מאפשר לסביבת זמן הריצה של RenderScript לעבוד בצורה חלקה עם הזיכרון
בקשות אך לא יכולה להקצות באופן מפורש. התוצאה הסופית היא למעשה אותה תוצאה
שנקראה malloc
בשפת C. היתרון הנוסף הוא שהמכונה הווירטואלית של Android יכולה לבצע איסוף אשפה וגם
לשתף את הזיכרון עם שכבת זמן הריצה של RenderScript. קישור נדרש רק לזיכרון שמוקצה באופן דינמי. באופן סטטי
הזיכרון שהוקצה נוצר באופן אוטומטי לקוד ה-RenderScript בזמן הקומפילציה. ראו איור 1
לקבלת מידע נוסף על האופן שבו מתבצעת הקצאת זיכרון.
כדי לתמוך במערכת הקצאת הזיכרון הזו, יש קבוצה של ממשקי API שמאפשרים ל-VM של Android
להקצות זיכרון ולהציע פונקציונליות דומה לזו של קריאה ל-malloc
. הכיתות האלה
לתאר בעיקר את האופן שבו צריך להקצות את הזיכרון וגם לבצע את ההקצאה. לשיפור
להבין איך הכיתות האלה פועלות, כדאי לחשוב עליהן ביחס
שיחת malloc
שיכולה להיראות כך:
array = (int *)malloc(sizeof(int)*10);
אפשר לחלק את הקריאה malloc
לשני חלקים: גודל הזיכרון שמוקצה (sizeof(int)
),
וגם את מספר היחידות של הזיכרון שיש להקצות (10). במסגרת Android יש סיווגים לשני החלקים האלה
וגם מחלקה שתייצג את malloc
עצמה.
המחלקה Element
מייצגת את החלק (sizeof(int)
)
של הקריאה malloc
ומציין תא אחד בהקצאת זיכרון, למשל תא יחיד
ערך מסוג מספר ממשי (float) או מבנה. המחלקה Type
כוללת את Element
ואת מספר הרכיבים להקצאה (10 בדוגמה שלנו). אפשר לחשוב על Type
בתור
מערך של Element
. המחלקה Allocation
מבצעת את הפעולה
הקצאת הזיכרון על סמך Type
נתון, ומייצגת את הזיכרון שהוקצה בפועל.
ברוב המקרים, אין צורך להפעיל ישירות את ממשקי ה-API האלה להקצאת זיכרון. השכבה שמשתקפת
הכיתה מפיקה קוד לשימוש אוטומטי בממשקי ה-API, וכל מה שצריך לעשות כדי להקצות זיכרון הוא
שמוצהר באחד מסיווגי השכבות המשתקפים ואז מקשרים
מהזיכרון שנוצר Allocation
ל-RenderScript.
יש מצבים מסוימים שבהם תרצו להשתמש בכיתות האלה ישירות כדי להקצות זיכרון
למשל, טעינת מפת סיביות ממשאב או כשרוצים להקצות זיכרון לסמנים
מסוגים פרימיטיביים. אפשר לראות איך לעשות זאת
הקצאה וקישור של זיכרון לקטע RenderScript.
בטבלה הבאה מתוארים בצורה מפורטת יותר שלושת הסוגים של ניהול הזיכרון:
סוג אובייקט Android | תיאור |
---|---|
Element |
רכיב מתאר תא אחד של הקצאת זיכרון ויכול להיות בשתי צורות: בסיסית או הוא מורכב. רכיב בסיסי מכיל רכיב יחיד של נתונים מכל סוג נתונים חוקי של RenderScript.
דוגמאות לסוגי נתונים בסיסיים של אלמנטים כוללות ערך יחיד של אלמנטים מורכבים כוללים רשימה של אלמנטים בסיסיים והם נוצרים מתוך
הנכסים מסוג ' |
Type |
סוג הוא תבנית להקצאת זיכרון שמורכבת מרכיב אחד או יותר
מאפיינים. הוא מתאר את פריסת הזיכרון (בעיקרון מערך של סוג מורכב מחמישה מאפיינים: X, Y, Z, LOD (רמת פירוט) ופנים (של קובייה מפה). אפשר להגדיר את המימדים X,Y,Z לכל ערך של מספר שלם חיובי במסגרת ומגבלות של הזיכרון הזמין. להקצאת מאפיין יחיד יש מאפיין X של גדול מאפס ואילו המאפיינים Y ו-Z הם אפס כדי לציין שהם לא קיימים. עבור לדוגמה, הקצאה של x=10, y=1 נחשבת לדו-ממדית ו-x=10, y=0 הוא מוגדר כחד ממדי. המאפיינים LOD ו-Faces הם בוליאניים כדי לציין שהם קיימים או לא קיים. |
Allocation |
הקצאה מספקת את הזיכרון לאפליקציות על סמך תיאור של הזיכרון
שמיוצג על ידי ההעלאה של נתוני הקצאה מתבצעת באחת משתי דרכים עיקריות: הסוג מסומן והסוג לא מסומן.
במערכים פשוטים יש פונקציות |
איך לעבוד עם הזיכרון
משתנים גלובליים לא סטטיים שמצהירים עליהם ב-RenderScript מקבלים זיכרון בזמן ההידור.
אפשר לעבוד עם המשתנים האלה ישירות בקוד RenderScript בלי שתצטרכו להקצות
את הזיכרון ברמת ה-framework של Android. לשכבת ה-framework של Android יש גם גישה למשתנים האלה
באמצעות ה-methods של רכיב ה-accessor, שנוצרות במחלקות השכבות משתקפות. אם המשתנים האלה
בשכבת זמן הריצה של RenderScript, הערכים האלה משמשים לאתחול
בשכבת המסגרת של Android. אם משתנים גלובליים מסומנים כקבועים, השיטה set
מסמן
לא נוצרו. כאן אפשר למצוא פרטים נוספים.
הערה: אם אתם משתמשים במבנים מסוימים של RenderScript שמכילים סמנים, כמו
rs_program_fragment
ו-rs_allocation
, עליך לקבל אובייקט
קודם המחלקה המתאימה של Android framework ואז קוראים לשיטה set
בשביל זה
כדי לקשר את הזיכרון לסביבת זמן הריצה של RenderScript. אי אפשר לשנות את המבנים האלה ישירות
בשכבת זמן הריצה של RenderScript. ההגבלה הזו לא רלוונטית למבנים שהוגדרו על ידי המשתמש
מכילים מצביעים, כי אי אפשר לייצא אותם למחלקה של שכבה מוחזרת
מלכתחילה. תיווצר שגיאת מהדר אם תנסו להצהיר על גרסה גלובלית לא סטטית
שמכיל את מצביע העכבר.
ל-RenderScript יש גם תמיכה בסמנים, אבל צריך להקצות את הזיכרון באופן מפורש.
קוד מסגרת של Android. כשמצהירים על מצביע גלובלי בקובץ .rs
,
להקצות זיכרון באמצעות מחלקה המתאימה של השכבה הבאה, ולחבר את הזיכרון הזה
לשכבת ה-RenderScript. אפשר להשתמש בזיכרון הזה משכבת ה-framework של Android וגם
לשכבת RenderScript, שמציעה את הגמישות לשנות את המשתנים
או שכבות מתאימות.
הקצאה וקישור של זיכרון דינמי ל-RenderScript
כדי להקצות זיכרון דינמי, צריך לקרוא ל-constructor של
הכיתה Script.FieldBase
, זו הדרך הנפוצה ביותר. חלופה היא ליצור
Allocation
באופן ידני, שנדרש עבור דברים כמו מצביעי טיפוס פרימיטיבי. אתם צריכים
להשתמש ב-constructor של מחלקה Script.FieldBase
כשהדבר אפשרי, כדי לשמור על פשטות.
לאחר קבלת הקצאת זיכרון, קוראים ל-method 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 framework.
זיכרון שמוקצה באופן סטטי מגיע עם הגבלת תקשורת חד-כיוונית
ברמת זמן הריצה של RenderScript. כשקוד RenderScript משנה את הערך של משתנה, הוא לא
הועברה לשכבת ה-framework של Android למטרות יעילות. הערך האחרון
שמוגדר מ-framework של Android תמיד מוחזר במהלך קריאה ל-get
. עם זאת, כשקוד framework של Android משנה משתנה, אפשר להעביר את השינוי הזה אל
שזמן הריצה של RenderScript סונכרן באופן אוטומטי או מסתנכרן במועד מאוחר יותר. אם צריך לשלוח נתונים
מסביבת זמן הריצה של RenderScript לשכבת ה-framework של Android, אפשר להשתמש
הפונקציה rsSendToClient()
כדי להתגבר על המגבלה הזו.
כשעובדים עם זיכרון שמוקצה באופן דינמי, השינויים בשכבת זמן הריצה של RenderScript מופצים בחזרה לשכבת ה-framework של Android, אם שיניתם את הקצאת הזיכרון באמצעות המצביע המשויך אליה. שינוי אובייקט בשכבת ה-framework של Android מופץ באופן מיידי את השינוי בחזרה ל-RenderScript בשכבת זמן הריצה.
קריאה וכתיבה למשתנים גלובליים
הקריאה וכתיבה למשתנים גלובליים היא תהליך פשוט. אפשר להשתמש ב-methods של רכיב הגישה ברמת ה-framework של Android, או להגדיר אותם ישירות בקוד RenderScript. חשוב לזכור שבכל פעם השינויים שמבצעים בקוד RenderScript לא מופצים חזרה לשכבת המסגרת של Android (חיפוש כאן לקבלת פרטים נוספים).
לדוגמה, בהינתן המבנה הבא שהוצהר בקובץ בשם rsfile.rs
:
typedef struct Point { int x; int y; } Point_t; Point_t point;
אפשר להקצות ערכים למבנה כמו זה ישירות ב-rsfile.rs
. הערכים האלה אינם
הוחזרו לרמת ה-framework של Android:
point.x = 1; point.y = 1;
אפשר להקצות ערכים למבנה בשכבת ה-framework של 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);
אפשר לקרוא את הערכים בשכבת ה-framework של Android עם הקוד הבא. חשוב לזכור הפונקציה מחזיר ערך רק אם הוגדר ערך ברמת ה-framework של Android. יתקבל מצביע null חריג אם מגדירים את הערך רק ברמת זמן הריצה של 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());
קריאה וכתיבה של מיקום הסמן של משתמשים אחרים
בהנחה שהזיכרון הוקצה ברמת ה-framework של Android וקשור לזמן הריצה של RenderScript,
אפשר לקרוא ולכתוב זיכרון ברמת ה-framework של Android באמצעות השיטות get
ו-set
לאותו מצביע.
בשכבה של סביבת זמן הריצה של RenderScript, אפשר לקרוא ולכתוב לזיכרון באמצעות מצביעים כרגיל, והשינויים מופצים.
בחזרה לשכבת ה-framework של 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;
אפשר גם לקרוא ולכתוב ערכים לסמן בשכבת ה-framework של 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 בכל פעם שאתם מבצעים שינוי בערך.