ย้ายข้อมูลจาก RenderScript

เราจะเลิกใช้งาน RenderScript API ใน Android 12 เป็นต้นไป อุปกรณ์และคอมโพเนนต์ ซึ่งผู้ผลิตส่วนใหญ่ก็หยุดให้การสนับสนุน การเร่งฮาร์ดแวร์แล้ว และคาดว่าจะนำการรองรับ RenderScript ออกทั้งหมดในเวอร์ชันในอนาคต

ประสิทธิภาพของ C/C++ อาจเพียงพอสำหรับการใช้งานในหลายกรณี และหากคุณ ซึ่งใช้ RenderScript เพื่อแทรกองค์ประกอบภายใน คุณสามารถแทนที่การใช้งานเหล่านั้นด้วย RenderScript Intrinsics Replacement Toolkit ซึ่งทำได้ง่ายกว่า เพื่อใช้งานและยังอาจได้รับการปรับปรุงประสิทธิภาพเพิ่มขึ้นถึง 2 เท่า

หากคุณต้องการใช้ประโยชน์จากการเร่ง GPU อย่างเต็มที่ เราขอแนะนำ การย้ายข้อมูลสคริปต์ไปยัง Vulkan, ตัวเลือกอื่นๆ แบบเร่ง รวมถึงการย้ายข้อมูลสคริปต์ไปยัง OpenGL โดยใช้แบบ Canvas การทำงานกับรูปภาพ หรือใช้ประโยชน์จากการลงสีกราฟิกของ Android ภาษา (AGSL)

หลังจากเลิกใช้งาน RenderScript ในแพลตฟอร์ม Android แล้ว การรองรับสำหรับ กำลังนำ RenderScript ออกในปลั๊กอิน Android Gradle เริ่มต้นด้วย ปลั๊กอิน Android Gradle 7.2, เลิกใช้งาน RenderScript API แล้ว โฆษณาเหล่านี้ ทำงานต่อไปได้ แต่จะเรียกใช้คำเตือน จะไม่มี AGP เวอร์ชันในอนาคตอีกต่อไป รวมถึงการรองรับ Renderscript คู่มือนี้จะอธิบายวิธีย้ายข้อมูลจาก RenderScript

ย้ายข้อมูลจาก Intinsics

แม้ว่าฟังก์ชันภายในของ RenderScript จะยังคงทำงานต่อไปหลังจาก การเลิกใช้งาน RenderScript อาจดำเนินการบน CPU เท่านั้นแทนที่จะเป็น GPU

การดำเนินการบางส่วนจะมีตัวเลือกที่มีประสิทธิภาพมากขึ้นรวมอยู่ใน แพลตฟอร์มหรือในคลัง Jetpack

การทำงานของอิมเมจแบบเร่งในตัว

แพลตฟอร์ม Android รองรับการประมวลผลรูปภาพแบบเร่งที่สามารถ สามารถใช้กับรูปภาพ ซึ่งไม่เกี่ยวข้องกับภายใน RenderScript ตัวอย่างเช่น

  • รวม
  • เบลอ
  • เมตริกซ์สี
  • ปรับขนาด

การเบลอรูปภาพใน Android 12 ขึ้นไปให้เป็น View

RenderEffect ที่รองรับการเบลอเพิ่มเข้ามาใน Android 12 API ระดับ 31 ที่จะช่วยให้คุณเบลอ RenderNode ได้ RenderNode เป็นโครงสร้างของรายการ Display ที่ Android ใช้เพื่อช่วยเพิ่ม กราฟิกแพลตฟอร์ม

Android มีทางลัดเพื่อใช้เอฟเฟกต์กับ RenderNode ที่เชื่อมโยง ด้วย View หากต้องการเบลอ View โปรดโทร View.setRenderEffect():

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

รูปภาพที่เบลอใน Android 12 ขึ้นไปที่แสดงผลเป็นบิตแมป

หากต้องการให้รูปภาพเบลอที่แสดงผลเป็น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) เพื่อ กำหนดลักษณะการทำงานของ Programmable ออบเจ็กต์ RuntimeShader AGSL แชร์ไวยากรณ์ส่วนใหญ่กับตัวปรับแสงเงา Fragment ของ 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 แบบระนาบเป็น RGB โดยใช้ CameraX

กำลังแปลงจาก YUV ระนาบ เป็น RGB เพื่อใช้ในการประมวลผลรูปภาพได้รับการสนับสนุนโดยเป็นส่วนหนึ่งของ กรณีการใช้งาน ImageAnalysis ภายใน Jetpack CameraX

มีแหล่งข้อมูลเกี่ยวกับการใช้ ImageAnalysis เป็นส่วนหนึ่งของ เริ่มต้นใช้งาน CameraX Codelab และใน กล้อง Android ตัวอย่าง

ชุดเครื่องมือแทนที่ภายใน Renderscript

หากแอปพลิเคชันของคุณใช้ภายใน คุณสามารถใช้การเปลี่ยนแบบสแตนด์อโลนได้ คลัง; การทดสอบของเราชี้ให้เห็นว่าสามารถเร็วกว่าการใช้ CPU ของ RenderScript ที่มีอยู่ การใช้งานของคุณ

ชุดเครื่องมือประกอบด้วยฟังก์ชันการทำงานต่อไปนี้

  • รวม
  • เบลอ
  • เมตริกซ์สี
  • คอนโวล์ฟ
  • ฮิสโตแกรมและฮิสโตแกรมดอท
  • ตารางตรวจสอบ (LUT) และ LUT แบบ 3 มิติ
  • ปรับขนาด
  • 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 รองรับ GPU ประมวลผลบน Vulkan และ OpenGL ES (GLES) API แบบข้ามแพลตฟอร์ม คุณอาจเห็นสิ่งนี้ ไม่จำเป็น เนื่องจากในอุปกรณ์ส่วนใหญ่ สคริปต์ของคุณทำงานอยู่บน CPU อยู่แล้ว แทน 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 สำหรับ GLES API คุณจึงไม่จำเป็นต้องใช้ NDK เมื่อทำงานใน OpenGL ES

Vulkan ไม่มีการเชื่อมโยง SDK จึงไม่มีการแมปโดยตรงจาก RenderScript ไปยัง Vulkan คุณเขียนโค้ด Vulkan โดยใช้ NDK และสร้าง JNI เข้าถึงโค้ดนี้จาก Kotlin หรือ Java

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