نقل البيانات من RenderScript

تم إيقاف واجهات برمجة تطبيقات RenderScript بدءًا من نظام التشغيل Android 12. وقد توقف مصنعو الأجهزة والمكونات بالفعل عن توفير دعم تسريع الأجهزة، ومن المتوقع أن تتم إزالة دعم RenderScript بالكامل في إصدار مستقبلي.

قد يكون أداء لغة C/C++ مناسبًا للعديد من حالات الاستخدام، وإذا كنت تعتمد فقط على RenderScript للركائز الأساسية، يمكنك استبدال هذه الاستخدامات بمجموعة أدوات الاستبدال مع RenderScript Intrinsics، التي يسهل استخدامها وتوفّر لك تحسينات محتملة بمقدار مرتين.

إذا كنت تريد الاستفادة بشكل كامل من تسريع وحدة معالجة الرسومات، ننصحك بنقل نصوصك البرمجية إلى Vulkan. تشمل الخيارات السريعة الأخرى نقل نصوصك البرمجية إلى OpenGL أو استخدام عمليات الصور المستندة إلى لوحة الرسم أو الاستفادة من لغة تظليل رسومات Android (AGSL).

بعد إيقاف RenderScript نهائيًا في نظام Android الأساسي، سيتم إيقاف استخدام RenderScript في مكوّن Android Gradle الإضافي. بدءًا من الإصدار 7.2 من مكوّن Gradle الإضافي لنظام التشغيل Android، تم إيقاف واجهات برمجة تطبيقات RenderScript. تستمر في العمل، ولكنها تستدعي التحذيرات. لن تتضمّن الإصدارات المستقبلية من AGP دعم Renderscript بعد الآن. يوضّح هذا الدليل كيفية الانتقال من RenderScript.

نقل البيانات من الأساسيات

رغم أنّ الدوال الأساسية لـ RenderScript تستمر في العمل بعد إيقاف RenderScript نهائيًا، قد يتم تنفيذها على وحدة المعالجة المركزية (CPU) فقط بدلاً من وحدة معالجة الرسومات.

تتوفّر في بعض هذه العمليات خيارات أكثر فعالية مدمَجة الآن في النظام الأساسي أو في مكتبات Jetpack.

عمليات الصور السريعة المُدمَجة

يدعم نظام Android الأساسي عمليات المعالجة السريعة للصور التي يمكن تطبيقها على الصور، بشكل مستقل عن دوال RenderScript. وتشمل الأمثلة ما يلي:

  • مزيج
  • التمويه
  • مصفوفة الألوان
  • تغيير الحجم

تمويه الصور على الإصدار 12 من نظام التشغيل Android والإصدارات الأحدث

تمت إضافة RenderEffect مع دعم التمويه إلى Android 12، المستوى 31 من واجهة برمجة التطبيقات، مما يتيح لك تمويه RenderNode. RenderNode هو تركيبة لقائمة العرض التي يستخدمها Android للمساعدة في تسريع رسومات النظام الأساسي.

يوفر Android اختصارًا لتطبيق أي تأثير على RenderNode المرتبط بالسمة View. لتمويه View، اتصِل بـ View.setRenderEffect():

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

تمويه الصور على الإصدار 12 من نظام التشغيل Android والإصدارات الأحدث عند عرض صورة نقطية

إذا كنت بحاجة إلى عرض الصورة المموّهة في 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 (AGSL) لتحديد سلوك عناصر RuntimeShader القابلة للبرمجة. تشارك لغة AGSL الكثير من تركيبتها مع أدوات تظليل أجزاء GLSL، ولكنّها تعمل ضمن نظام عرض رسومات Android لتخصيص الرسم داخل Canvas وفلترة محتوى View. يمكن استخدام ذلك لإضافة معالجة مخصّصة للصور أثناء عمليات الرسم، أو من خلال استخدام علامة RenderNode مباشرةً لعرض صورة في لوحة Bitmap. يوضح المثال التالي كيفية تطبيق أداة تظليل مخصصة لاستبدال تأثير تمويه الصورة.

ابدأ بإنشاء RuntimeShader، ثم أنشِئ مثيلاً له باستخدام رمز مُظلل 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 عادي إلى نموذج أحمر أخضر أزرق باستخدام CameraX

إنّ التحويل من تنسيق YUV مستوٍ إلى نموذج أحمر أخضر أزرق للاستخدام في معالجة الصور متاح كجزء من حالة استخدام تحليل الصورة داخل كاميرا CameraX في Jetpack.

يمكنك الاطّلاع على موارد حول كيفية استخدام ImageAnalysis كجزء من الدرس التطبيقي حول الترميز بدء استخدام CameraX وفي مستودع عيّنات كاميرا Android.

مجموعة أدوات استبدال العناصر الأساسية في Renderscript

إذا كان تطبيقك يستخدم أساسيات، يمكنك استخدام مكتبة الاستبدال المستقلة، حيث تشير اختباراتنا إلى أنها أسرع من طريقة استخدام وحدة المعالجة المركزية (CPU) الحالية في RenderScript.

تتضمن مجموعة الأدوات الوظائف التالية:

  • مزيج
  • التمويه
  • مصفوفة الألوان
  • التفاف
  • المدرّج التكراري والمدرّج التكراري
  • جدول البحث (LUT) وجدول البحث الثلاثي الأبعاد (LUT)
  • تغيير الحجم
  • من YUV إلى نموذج أحمر أخضر أزرق

للحصول على التفاصيل الكاملة والقيود، يُرجى الاطّلاع على README.md وToolkit.kt في مجموعة الأدوات. .

اتّبِع الخطوات التالية لتنزيل المكتبة وإضافتها واستخدامها:

  1. نزِّل المشروع من GitHub.

  2. حدِّد موقع renderscript-toolkit module وأنشئه.

  3. أضِف المكتبة إلى مشروعك على "استوديو Android" عن طريق تعديل ملف build.gradle في التطبيق.

  4. استدعِ الطريقة المناسبة لمجموعة الأدوات.

مثال: النقل من الدالة ScriptIntrinsicBlur

لاستبدال دالة ScriptIntrinsicBlur:

  • لتمويه صورة نقطية، يجب طلب الرقم Toolkit.blur.

    var blurredBitmap = Toolkit.blur(myBitmap, radius)
    
  • إذا كنت تريد تمويه صورة ممثلة بمجموعة من وحدات البايت، حدِّد العرض والارتفاع وعدد وحدات البايت لكل بكسل.

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

نقل البيانات من النصوص البرمجية

إذا تعذّر حل حالة الاستخدام باستخدام:

ويمكن أن تستفيد حالة الاستخدام من تسريع وحدة معالجة الرسومات، فيتيح نظام Android طريقة احتساب وحدة معالجة الرسومات على واجهات برمجة تطبيقات Vulkan وOpenGL ES (GLES) من عدّة منصات. وقد ترى ذلك غير ضروري، لأنّ نصوصك البرمجية تعمل في معظم الأجهزة على وحدة المعالجة المركزية (CPU) بدلاً من وحدة معالجة الرسومات، لأنّ لغة C/C++ قد تكون أسرع من طريقة احتساب RenderScript أو GLES أو Vulkan في بعض حالات الاستخدام. (أو على الأقل سريعة بما يكفي لحالة الاستخدام)

لفهم كيفية نقل البيانات بشكل أفضل، راجِع نموذج التطبيق. يوضّح النموذج طريقة تمويه صورة نقطية وتحويل مصفوفة الألوان في RenderScript، ويحتوي على رمز مكافئ في Vulkan وOpenGL.

إذا كان تطبيقك يحتاج إلى التوافق مع مجموعة من الإصدارات، استخدِم RenderScript للأجهزة التي تعمل بنظام التشغيل Android 6 (المستوى 23 لواجهة برمجة التطبيقات) والإصدارات الأقدم، وVulkan أو GLES على الأجهزة المتوافقة التي تعمل بالإصدار 7 من نظام التشغيل Android (المستوى 24 من واجهة برمجة التطبيقات) والإصدارات الأحدث. إذا كان عمر minSdkVersion هو 24 عامًا أو أكثر، قد لا تحتاج إلى استخدام RenderScript، إذ يمكن استخدام Vulkan أو GLES 3.1 في أي مكان تحتاج فيه إلى إجراء الحوسبة الحسابية لوحدة معالجة الرسومات.

يوفّر Android روابط SDK لواجهات برمجة تطبيقات GLES، لذلك ليس من الضروري استخدام NDK عند العمل في OpenGL ES.

لا يوفّر Vulkan روابط حِزم SDK، لذلك لا يمكن الربط المباشر بين RenderScript وVulkan، بل تكتب رمز Vulkan باستخدام NDK وتنشئ دوال JNI للوصول إلى هذا الرمز من Kotlin أو Java.

تتناول الصفحات التالية جوانب الانتقال من RenderScript. ينفذ نموذج التطبيق كل هذه الاعتبارات تقريبًا. لفهمها بشكل أفضل، يمكنك مقارنة الرمزين المكافئَين RenderScript وVulkan.