מעבר מ-RenderScript

ממשקי ה-API של RenderScript הוצאו משימוש החל מ-Android 12. מכשיר ורכיב יצרנים כבר הפסיקו לספק תמיכה בהאצת חומרה, והתמיכה ב-RenderScript צפויה להיות מוסרת לחלוטין בגרסה עתידית.

ביצועי C/C++ עשויים להספיק לתרחישי שימוש רבים, ואם שמסתמך על RenderScript לערכי הפנימיים, אפשר להחליף את השימושים האלה את RenderScript Intrinsics Replacement Toolkit, וקל יותר להשתמש בערכת הכלים לשימוש והוא מגיע עם שיפור אפשרי של פי 2 בביצועים!

אם אתה צריך להפיק את המרב מהאצת ה-GPU, העברת סקריפטים ל-Vulkan, אפשרויות מהירות אחרות כולל העברת הסקריפטים שלך ל-OpenGL, באמצעות גרסה מבוססת-לוח הציור פעולות תמונה, או שימוש בהצללה של הגרפיקה של Android שפה (AGSL).

לאחר ההוצאה משימוש של RenderScript בפלטפורמת Android, תמיכה ב אנחנו מסירים את RenderScript בפלאגין של Android Gradle. מתחיל ב- הפלאגין Android Gradle גרסה 7.2, ממשקי ה-API של RenderScript הוצאו משימוש. הם ימשיכו לפעול, אבל להפעיל אזהרות. גרסאות עתידיות של AGP לא יהיו יותר זמינות כולל תמיכה ב-Renderscript. במדריך הזה מוסבר איך להעביר מ- RenderScript.

העברה מפנימי

למרות שהפונקציות intrinsics של RenderScript ממשיכות לפעול אחרי להוצאה משימוש של RenderScript, יכול להיות שהם יופעלו רק ב-CPU, GPU.

בחלק מהפעולות האלה, יש עכשיו אפשרויות יעילות יותר שמובנות בפלטפורמה או בספריות Jetpack.

פעולות תמונה מואצות מובנות

פלטפורמת Android תומכת בפעולות עיבוד תמונות מואצות שיכולות להיות רלוונטיות לתמונות, ללא קשר למאפייני RenderScript. דוגמאות:

  • שילוב
  • טשטוש הרקע
  • מטריצת צבעים
  • שינוי הגודל

טשטוש תמונה ב-Android 12 ואילך בתצוגה

הסרטון RenderEffect עם תמיכה בטשטוש נוסף ל-Android 12, רמת API 31 מאפשרת לטשטש RenderNode. RenderNode הוא מבנה של רשימת תצוגה ש-Android משתמש בו כדי להאיץ גרפיקת פלטפורמה.

ב-Android יש קיצור דרך להחלת אפקט על RenderNode שמשויך למכשיר עם View. כדי לטשטש View, צריך להתקשר View.setRenderEffect():

val blurRenderEffect = RenderEffect.createBlurEffect(radius, radius,
        Shader.TileMode.MIRROR
    )
view.setRenderEffect(blurRenderEffect)

טשטוש תמונה ב-Android 12 ואילך שעבר רינדור למפת סיביות (bitmap)

אם אתם צריכים שהתמונה המטושטשת תעבור רינדור ל-Bitmap, המסגרת תומך ברינדור מואץ באמצעות HardwareRenderer מגובה על ידי HardwareBuffer. הקוד הבא יוצר HardwareRenderer, RenderNode ו-RenderEffect של הטשטוש:

val imageReader = ImageReader.newInstance(
    bitmap.width, bitmap.height,
    PixelFormat.RGBA_8888, numberOfOutputImages,
    HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT
)
val renderNode = RenderNode("BlurEffect")
val hardwareRenderer = HardwareRenderer()

hardwareRenderer.setSurface(imageReader.surface)
hardwareRenderer.setContentRoot(renderNode)
renderNode.setPosition(0, 0, imageReader.width, imageReader.height)
val blurRenderEffect = RenderEffect.createBlurEffect(
    radius, radius,
    Shader.TileMode.MIRROR
)
renderNode.setRenderEffect(blurRenderEffect)

כדי ליישם את האפקט הזה צריך להשתמש RecordingCanvas עבור RenderNode. את הקוד הבא רושם את השרטוט, יוצר את בקשת העיבוד ואז ממתין בקשה לסיום:

val renderCanvas = it.renderNode.beginRecording()
renderCanvas.drawBitmap(it.bitmap, 0f, 0f, null)
renderNode.endRecording()
hardwareRenderer.createRenderRequest()
    .setWaitForPresent(true)
    .syncAndDraw()

התמונה שעברה עיבוד נמצאת במסגרת HardwareBuffer שמשויך אל ImageReader. הקוד הבא מקבל את Image מחזירה Bitmap שעטוף ב-HardwareBuffer.

val image = imageReader.acquireNextImage() ?: throw RuntimeException("No Image")
val hardwareBuffer = image.hardwareBuffer ?: throw RuntimeException("No HardwareBuffer")
val bitmap = Bitmap.wrapHardwareBuffer(hardwareBuffer, null)
    ?: throw RuntimeException("Create Bitmap Failed")

הקוד הבא ינקה אחרי עיבוד התמונה. שימו לב ניתן להשתמש ב-ImageReader, ב-RenderNode, ב-RenderEffect וב-HardwareRenderer כדי לעבד כמה תמונות.

hardwareBuffer.close()
image.close()
imageReader.close()
renderNode.discardDisplayList()
hardwareRenderer.destroy()

AGSL לעיבוד תמונה

מערכת Android 13 ואילך משמשת את Android Graphics Shading Language (AGSL) כדי: להגדיר את ההתנהגות של RuntimeShader אובייקטים. AGSL משתף חלק גדול מהתחביר שלו עם תוכנות הצללה של מקטעים ב-GLSL, אבל הוא פועל מערכת רינדור גרפיקה ב-Android להתאמה אישית של ציור בתוך Canvas ולסנן View תוכן. אפשר להשתמש באפשרות הזו כדי להוסיף עיבוד תמונה בהתאמה אישית במהלך פעולות שרטוט, או על ידי שימוש ישירות ב-RenderNode כדי לעבד את התמונה בתוך אזור עריכה של Bitmap. הדוגמה הבאה ממחישה איך להחיל את התג כלי הצללה בהתאמה אישית שיחליף את אפקט טשטוש התמונה.

התחל על ידי יצירת RuntimeShader, יצירת מופע כזה באמצעות תוכנת ההצללה (shader) של AGSL כלי ההצללה הזה משמש להחלת מטריצת צבעים לסיבוב גוונים:

val hueShader = RuntimeShader("""
    uniform float2 iResolution;       // Viewport resolution (pixels)
    uniform float2 iImageResolution;  // iImage1 resolution (pixels)
    uniform float iRadian;            // radian to rotate things around
    uniform shader iImage1;           // An input image
    half4 main(float2 fragCoord) {
    float cosR = cos(iRadian);
    float sinR = sin(iRadian);
        mat4 hueRotation =
        mat4 (
                0.299 + 0.701 * cosR + 0.168 * sinR, //0
                0.587 - 0.587 * cosR + 0.330 * sinR, //1
                0.114 - 0.114 * cosR - 0.497 * sinR, //2
                0.0,                                 //3
                0.299 - 0.299 * cosR - 0.328 * sinR, //4
                0.587 + 0.413 * cosR + 0.035 * sinR, //5
                0.114 - 0.114 * cosR + 0.292 * sinR, //6
                0.0,                                 //7
                0.299 - 0.300 * cosR + 1.25 * sinR,  //8
                0.587 - 0.588 * cosR - 1.05 * sinR,  //9
                0.114 + 0.886 * cosR - 0.203 * sinR, //10
                0.0,                                 //11
                0.0, 0.0, 0.0, 1.0 );                //12,13,14,15
        float2 scale = iImageResolution.xy / iResolution.xy;
        return iImage1.eval(fragCoord * scale)*hueRotation;
    }
""")

אפשר להחיל את תוכנת ההצללה על RenderNode, בדיוק כמו על כל RenderEffect אחר. הדוגמה הבאה ממחישה איך להגדיר את המדים ב-hueShader:

hueShader.setFloatUniform("iImageResolution", bitmap.width.toFloat(),
    bitmap.height.toFloat())
hueShader.setFloatUniform("iResolution", bitmap.width.toFloat(),
    bitmap.height.toFloat())
hueShader.setFloatUniform("iRadian", radian)
hueShader.setInputShader( "iImage1", BitmapShader(bitmap, Shader.TileMode.MIRROR,
    Shader.TileMode.MIRROR))
val colorFilterEffect = RenderEffect.createShaderEffect(it.hueShader)
renderNode.setRenderEffect(colorFilterEffect)

כדי לקבל את הBitmap, משתמשים באותה שיטה כמו בטשטוש התמונה הקודם לדוגמה.

  • התוכן הפנימי RecordingCanvas עבור RenderNode מפעיל את ההצללה.
  • ה-Image נרכש ומחזיר Bitmap שעוטף את HardwareBuffer.

המרה מ-YUV מישור ל-RGB באמצעות CameraX

המרה מ-YUV מישור ל-RGB לשימוש בעיבוד תמונות. תרחיש לדוגמה של ImageAnalysis ב-Jetpack מצלמהX.

יש משאבים בנושא השימוש ב-ImageAnalysis כחלק תחילת העבודה עם Codelab של CameraX ובתוך מצלמת Android דוגמאות.

ערכת הכלים להחלפה של רכיב Renderscript

אם האפליקציה שלך משתמשת בעקרונות פנימיים, אפשר להשתמש בהחלפה העצמאית. ספרייה; לפי הבדיקות שלנו, הוא מהיר יותר משימוש במעבד (CPU) הקיים של RenderScript יישום בפועל.

ערכת הכלים כוללת את הפונקציות הבאות:

  • שילוב
  • טשטוש הרקע
  • מטריצת צבעים
  • קעור
  • היסטוגרמה והיסטוגרמה
  • טבלת חיפוש (LUT) ו-LUT 3D
  • שינוי הגודל
  • YUV ל-RGB

לקבלת פרטים מלאים ומגבלות, אפשר לעיין בREADME.md ובToolkit.kt של ערכת הכלים. .

מבצעים את השלבים הבאים כדי להוריד את הספרייה, להוסיף אותה ולהשתמש בה:

  1. מורידים את הפרויקט. מ-GitHub.

  2. מאתרים ובונים את renderscript-toolkit module.

  3. כדי להוסיף את הספרייה לפרויקט Android Studio, משנים את הגדרות האפליקציה קובץ build.gradle.

  4. מפעילים את השיטה המתאימה של ערכת הכלים.

דוגמה: העברה מהפונקציה ScriptIntrinsicBlur

כדי להחליף את הפונקציה ScriptIntrinsicBlur:

  • כדי לטשטש מפת סיביות, צריך להתקשר למספר Toolkit.blur.

    var blurredBitmap = Toolkit.blur(myBitmap, radius)
    
  • אם רוצים לטשטש תמונה שמיוצגת על ידי מערך של בייטים, מציינים את הערך הרוחב, הגובה ומספר הבייטים לכל פיקסל.

    val outArray = Toolkit.blur(inputArray, bytesPerPixel, width, height, radius)
    

העברה מסקריפטים

אם לא ניתן לפתור את התרחיש לדוגמה באמצעות:

בנוסף, האצת GPU ב-Android יכולה להועיל לתרחיש לדוגמה שלכם מחשב בממשקי API של Vulkan ו-OpenGL ES (GLES) בפלטפורמות שונות. המידע הזה יכול להופיע אין צורך, כי ברוב המכשירים הסקריפטים כבר מריצים על המעבד במקום ה-GPU: C/C++ עשוי להיות מהיר יותר מ-RenderScript , GLES או Vulkan במקרים מסוימים. (או לפחות מהיר מספיק למקרה שלכם)

כדי להבין טוב יותר איך לבצע את ההעברה, כדאי לעיין אפליקציה לדוגמה. מדגם שמדגים איך גם לטשטש מפת סיביות וגם לבצע המרה של מטריצת צבעים ב-RenderScript, ויש לו קוד מקביל ב-Vulkan וב-OpenGL.

אם האפליקציה צריכה לתמוך במגוון גרסאות, אפשר להשתמש ב-RenderScript מכשירים עם Android 6 (API ברמה 23) ומטה, ו-Vulkan או GLES מכשירים נתמכים עם Android 7 (API ברמה 24) ואילך. אם הגיל של minSdkVersion הוא 24 ומעלה, יכול להיות שלא יהיה צורך להשתמש ב-RenderScript. Vulkan או ניתן להשתמש ב-GLES 3.1 בכל מקום שבו דרושה תמיכה במחשוב GPU.

מערכת Android מספקת קישורי SDK עבור ממשקי API של GLES, לכן לא צריך להשתמש NDK כשעובדים ב-OpenGL ES.

Vulkan לא מספק קישורי SDK, ולכן אין מיפוי ישיר מ- RenderScript ל-Vulkan; כותבים את קוד ה-Vulkan באמצעות ה-NDK ויוצרים JNI כדי לגשת לקוד הזה מ-Kotlin או Java.

בדפים הבאים מתוארים היבטים של המעבר מ-RenderScript. הדוגמה מטמיעים את כל השיקולים האלה. כדי להבין אותם טוב יותר, להשוות בין הקוד RenderScript ו-Vulkan המקביל.