مهاجرت از RenderScript

APIهای RenderScript از Android 12 منسوخ شده‌اند. سازندگان دستگاه‌ها و قطعات قبلاً ارائه پشتیبانی از شتاب سخت‌افزاری را متوقف کرده‌اند و انتظار می‌رود پشتیبانی RenderScript در نسخه‌های آینده به طور کامل حذف شود.

عملکرد C/C++ ممکن است برای بسیاری از موارد استفاده کافی باشد، و اگر فقط به RenderScript برای Intrinsics متکی بودید، می‌توانید آن استفاده‌ها را با RenderScript Intrinsics Replacement Toolkit جایگزین کنید، که استفاده آسان‌تر است و با بهبود عملکرد ۲ برابری همراه است!

اگر نیاز به استفاده کامل از شتاب GPU دارید، توصیه می‌کنیم اسکریپت‌های خود را به Vulkan منتقل کنید ، سایر گزینه‌های تسریع‌شده عبارتند از انتقال اسکریپت‌های خود به OpenGL ، استفاده از عملیات تصویر مبتنی بر Canvas ، یا استفاده از زبان سایه‌زنی گرافیک اندروید (AGSL) .

پس از منسوخ شدن RenderScript در پلتفرم اندروید، پشتیبانی از RenderScript در افزونه Android Gradle حذف می شود. با شروع پلاگین Android Gradle 7.2، API های RenderScript منسوخ شده اند. آنها به کار خود ادامه می دهند، اما هشدارها را فرا می خوانند. نسخه های آینده AGP دیگر شامل پشتیبانی Renderscript نخواهد بود. این راهنما نحوه مهاجرت از RenderScript را توضیح می دهد.

مهاجرت از درونی

اگرچه توابع ذاتی RenderScript پس از منسوخ شدن RenderScript همچنان به کار خود ادامه می دهند، اما ممکن است به جای GPU فقط روی CPU اجرا شوند.

برای برخی از این عملیات، اکنون گزینه های کارآمدتری در پلتفرم یا کتابخانه های Jetpack تعبیه شده است.

عملیات تصویری شتاب یافته داخلی

پلتفرم اندروید از عملیات پردازش تصویر سریع پشتیبانی می‌کند که می‌توان آن‌ها را مستقل از ذاتی RenderScript روی تصاویر اعمال کرد. مثالها عبارتند از:

  • مخلوط کنید
  • محو کردن
  • ماتریس رنگ
  • تغییر اندازه
تاری تصویر در Android 12 و بالاتر به نمای

RenderEffect با پشتیبانی از blur به اندروید 12، سطح API 31 اضافه شد و به شما امکان می دهد یک RenderNode محو کنید. RenderNode ساختاری از لیست نمایشگر است که اندروید از آن برای کمک به سرعت بخشیدن به گرافیک پلت فرم استفاده می کند.

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 Graphics Shading Language (AGSL) توسط Android 13+ برای تعریف رفتار اشیاء RuntimeShader قابل برنامه ریزی استفاده می شود. AGSL قسمت اعظم نحو خود را با شیدرهای قطعه GLSL به اشتراک می گذارد، اما در سیستم رندر گرافیکی اندروید کار می کند تا هم نقاشی را در 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;
    }
""")

سایه زن را می توان مانند هر RenderEffect دیگری روی یک RenderNode اعمال کرد. مثال زیر نحوه تنظیم یونیفرم ها را در 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 آن را می‌پیچد.
با استفاده از CameraX از YUV مسطح به RGB تبدیل کنید

تبدیل از YUV مسطح به RGB برای استفاده در پردازش تصویر به عنوان بخشی از مورد استفاده ImageAnalysis در CameraX Jetpack پشتیبانی می شود.

منابعی در مورد استفاده از ImageAnalysis به عنوان بخشی از شروع به کار با Codelab CameraX و در مخزن نمونه دوربین اندروید وجود دارد.

جعبه ابزار جایگزینی Renderscript intrinsics

اگر برنامه شما از Intrinsics استفاده می کند، می توانید از کتابخانه جایگزین مستقل استفاده کنید. آزمایش‌های ما نشان می‌دهد که سریع‌تر از اجرای CPU RenderScript موجود است.

جعبه ابزار شامل توابع زیر است:

  • مخلوط کنید
  • محو کردن
  • ماتریس رنگ
  • در هم بپیچید
  • هیستوگرام و هیستوگرام نقطه
  • جدول جستجو (LUT) و LUT 3D
  • تغییر اندازه
  • YUV به RGB

برای جزئیات کامل و محدودیت‌ها، به README.md و Toolkit.kt جعبه ابزار مراجعه کنید. فایل ها

برای دانلود، افزودن و استفاده از کتابخانه مراحل زیر را انجام دهید:

  1. پروژه را از گیت هاب دانلود کنید.

  2. renderscript-toolkit module را بیابید و بسازید.

  3. با تغییر فایل build.gradle برنامه، کتابخانه را به پروژه Android Studio خود اضافه کنید.

  4. روش مناسب جعبه ابزار را فراخوانی کنید.

مثال: از تابع ScriptIntrinsicBlur مهاجرت کنید

برای جایگزینی تابع ScriptIntrinsicBlur :

  • برای محو کردن یک بیت مپ، با Toolkit.blur تماس بگیرید.

    var blurredBitmap = Toolkit.blur(myBitmap, radius)
    
  • اگر می خواهید تصویری را که با آرایه ای از بایت ها نمایش داده می شود تار کنید، عرض، ارتفاع و تعداد بایت ها در هر پیکسل را مشخص کنید.

    val outArray = Toolkit.blur(inputArray, bytesPerPixel, width, height, radius)
    
مهاجرت از اسکریپت ها

اگر مورد استفاده شما با موارد زیر قابل حل نیست:

و، مورد استفاده شما می تواند از شتاب GPU بهره مند شود، Android از محاسبه GPU در بین پلتفرم های Vulkan و OpenGL ES (GLES) API پشتیبانی می کند. ممکن است این کار را غیرضروری بدانید زیرا در اکثر دستگاه‌ها اسکریپت‌های شما از قبل به جای GPU بر روی CPU اجرا می‌شوند: 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 را برای GLES API ارائه می‌کند، بنابراین استفاده از NDK هنگام کار در OpenGL ES ضروری نیست.

Vulkan اتصالات SDK را ارائه نمی دهد، بنابراین هیچ نقشه مستقیمی از RenderScript به Vulkan وجود ندارد. شما کد Vulkan را با استفاده از NDK می نویسید و توابع JNI را برای دسترسی به این کد از Kotlin یا Java ایجاد می کنید.

صفحات زیر جنبه های مهاجرت از RenderScript را پوشش می دهند. برنامه نمونه تقریباً همه این ملاحظات را اجرا می کند. برای درک بهتر آنها، کدهای معادل RenderScript و Vulkan را با هم مقایسه کنید.