ย้ายสคริปต์ไปยัง OpenGL ES 3.1

สำหรับภาระงานที่การประมวลผล GPU เหมาะสมที่สุด ให้ย้ายข้อมูลสคริปต์ RenderScript ไปยัง OpenGL ES (GLES) อนุญาตแอปพลิเคชันที่เขียนด้วยภาษา Kotlin, Java หรือการใช้ NDK เพื่อใช้ประโยชน์จากฮาร์ดแวร์ GPU ภาพรวมระดับสูงต่อไปนี้ช่วยให้คุณใช้ตัวปรับเงาการประมวลผล OpenGL ES 3.1 เพื่อ แทนที่สคริปต์ RenderScript

การเริ่มต้น GLES

ทำตามขั้นตอนต่อไปนี้แทนการสร้างออบเจ็กต์บริบท RenderScript เพื่อสร้างบริบทนอกหน้าจอ GLES โดยใช้ EGL ดังนี้

  1. รับจอแสดงผลเริ่มต้น

  2. เริ่มต้น EGL โดยใช้การแสดงผลเริ่มต้น โดยระบุเวอร์ชัน GLES

  3. เลือกการกำหนดค่า EGL ที่มีประเภทแพลตฟอร์มเป็น EGL_PBUFFER_BIT

  4. ใช้การแสดงผลและการกำหนดค่าเพื่อสร้างบริบท EGL

  5. สร้างพื้นผิวนอกหน้าจอด้วย eglCreatePBufferSurface หากบริบท จะใช้สำหรับการประมวลผลเท่านั้น ซึ่งอาจจะมีขนาดเล็กเล็กน้อย (1x1) แพลตฟอร์ม

  6. สร้างเธรดการแสดงผลและเรียก eglMakeCurrent ในเธรดการแสดงผลพร้อมจอแสดงผล พื้นผิว และบริบท EGL เพื่อเชื่อมโยงบริบท GL กับชุดข้อความ

แอปตัวอย่างสาธิตวิธีการเริ่มต้นบริบท GLES ใน GLSLImageProcessor.kt ดูข้อมูลเพิ่มเติมได้ที่ EGLSurfaces และ OpenGL ES

เอาต์พุตการแก้ไขข้อบกพร่อง GLES

การได้รับข้อผิดพลาดที่เป็นประโยชน์จาก OpenGL ใช้ส่วนขยายเพื่อเปิดใช้การบันทึกการแก้ไขข้อบกพร่อง ที่ตั้งค่า Callback ที่เป็นเอาต์พุตการแก้ไขข้อบกพร่อง ใช้วิธีการจาก SDK glDebugMessageCallbackKHR เป็นไปไม่ได้เลย และมีการ ข้อยกเว้น ตัวอย่างแอป จะมี Wrapper สําหรับการติดต่อกลับจากโค้ด NDK

การจัดสรร GLES

การจัดสรร RenderScript สามารถย้ายข้อมูลไปที่ พื้นผิวการเก็บข้อมูลที่เปลี่ยนแปลงไม่ได้ หรือ ออบเจ็กต์บัฟเฟอร์พื้นที่เก็บข้อมูล Shader สำหรับรูปภาพแบบอ่านอย่างเดียว คุณ ใช้ออบเจ็กต์ตัวอย่าง ซึ่งช่วยให้ดำเนินการต่อไปนี้ได้ ในการกรองด้วย

ทรัพยากร GLES จะได้รับการจัดสรรภายใน GLES เพื่อหลีกเลี่ยงการคัดลอกความทรงจำ ค่าใช้จ่ายในการดำเนินการเมื่อโต้ตอบกับ คอมโพเนนต์อื่นๆ ของ Android สำหรับรูปภาพ KHR ที่อนุญาตให้แชร์ ของข้อมูลภาพในอาร์เรย์ 2 มิติ ต้องใช้ส่วนขยายนี้สำหรับอุปกรณ์ Android เริ่มต้นด้วย Android 8.0 แกนประมวลผลกราฟิก ไลบรารี Android Jetpack มีการรองรับการสร้างรูปภาพเหล่านี้ภายในโค้ดที่มีการจัดการและการแมป ไปยัง HardwareBuffer ที่จัดสรรไว้:

val outputBuffers = Array(numberOfOutputImages) {
  HardwareBuffer.create(
    width, height, HardwareBuffer.RGBA_8888, 1,
    HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
  )
}
val outputEGLImages = Array(numberOfOutputImages) { i ->
    androidx.opengl.EGLExt.eglCreateImageFromHardwareBuffer(
        display,
        outputBuffers[i]
    )!!
}

แต่ไม่ได้สร้างพื้นผิวพื้นที่เก็บข้อมูลที่เปลี่ยนแปลงไม่ได้สำหรับ ตัวปรับแสงเงาประมวลผลเพื่อเขียนลงในบัฟเฟอร์โดยตรง ตัวอย่างใช้ glCopyTexSubImage2D เพื่อคัดลอกพื้นผิวพื้นที่เก็บข้อมูลที่ใช้ โดยตัวปรับแสงเงาการประมวลผลลงใน KHR Image หากไดรเวอร์ OpenGL สนับสนุน ส่วนขยาย EGL Image Storage ตามด้วยส่วนขยายดังกล่าว สามารถใช้สร้างพื้นผิวพื้นที่เก็บข้อมูลที่เปลี่ยนแปลงไม่ได้ที่ใช้ร่วมกันเพื่อหลีกเลี่ยงการคัดลอก

การแปลงไปใช้เครื่องมือปรับแสงเงาการประมวลผล GLSL

ระบบจะแปลงสคริปต์ RenderScript ของคุณเป็นตัวปรับเงาการประมวลผล GLSL

เขียนตัวปรับแสงเงาการประมวลผล GLSL

ใน OpenGL ES ตัวปรับแสงเงาประมวลผลจะเขียนในส่วน ภาษาแรเงา OpenGL (GLSL)

การปรับตัวทั่วโลกของสคริปต์

คุณจะใช้เครื่องแบบก็ได้ โดยขึ้นอยู่กับลักษณะเฉพาะของสคริปต์ทั่วโลก หรือวัตถุบัฟเฟอร์แบบเดียวกันสำหรับส่วนกลางที่ไม่มีการปรับเปลี่ยนภายในตัวปรับแสงเงา:

  • บัฟเฟอร์แบบเดียวกัน: แนะนำสำหรับโกลบอลของสคริปต์ที่มีการเปลี่ยนแปลงบ่อยในขนาดที่ใหญ่กว่า ดันขีดจำกัดคงที่

สำหรับส่วนกลางที่มีการเปลี่ยนแปลงภายในตัวปรับแสงเงา คุณสามารถใช้ พื้นผิวการเก็บข้อมูลที่เปลี่ยนแปลงไม่ได้ หรือ ออบเจ็กต์บัฟเฟอร์พื้นที่เก็บข้อมูล Shader

ดำเนินการคำนวณ

ตัวปรับแสงเงาการประมวลผลไม่ได้เป็นส่วนหนึ่งของไปป์ไลน์กราฟิก มีจุดประสงค์ทั่วไป และออกแบบมาเพื่อคำนวณงานที่ โหลดพร้อมกันได้มาก วิธีนี้ช่วยให้คุณมี ควบคุมการทำงานของโฆษณาได้มากขึ้น แต่ก็หมายความว่าคุณจะต้อง เข้าใจได้มากขึ้นว่างานของคุณ ทำงานควบคู่กันไปอย่างไร

สร้างและเริ่มต้นโปรแกรมประมวลผล

การสร้างและการเริ่มต้นโปรแกรมประมวลผลมีสิ่งต่างๆ หลายอย่างเหมือนกับ การทำงานกับตัวปรับแสงเงา GLES อื่นๆ

  1. สร้างโปรแกรมและตัวปรับแสงเงาการประมวลผลที่เชื่อมโยงกับโปรแกรมดังกล่าว

  2. แนบแหล่งที่มาของตัวปรับแสงเงา คอมไพล์ตัวปรับแสงเงา (และตรวจสอบผลลัพธ์ ของวิดีโอรวม)

  3. แนบตัวปรับแสงเงา ลิงก์โปรแกรม และใช้โปรแกรมดังกล่าว

  4. สร้าง เริ่มต้น และเชื่อมโยงแบบเดียวกันทั้งหมด

เริ่มการคำนวณ

ตัวควบคุมเฉดสีการประมวลผลทำงานภายในพื้นที่แบบ 1 มิติ 2 มิติ หรือ 3 มิติในชุด สำหรับกลุ่มงาน ซึ่งกำหนดไว้ภายในซอร์สโค้ดของตัวปรับแสงเงา และแสดงถึง ขนาดการเรียกใช้ขั้นต่ำและรูปทรงเรขาคณิตของตัวปรับแสงเงา ตัวปรับแสงเงาต่อไปนี้ทำงานในภาพ 2 มิติและกำหนดกลุ่มงานเป็น 2 ส่วน ขนาด:

private const val WORKGROUP_SIZE_X = 8
private const val WORKGROUP_SIZE_Y = 8
private const val ROTATION_MATRIX_SHADER =
    """#version 310 es
    layout (local_size_x = $WORKGROUP_SIZE_X, local_size_y = $WORKGROUP_SIZE_Y, local_size_z = 1) in;

กลุ่มงานจะแชร์ความทรงจำได้ ซึ่งกำหนดโดย GL_MAX_COMPUTE_SHARED_MEMORY_SIZE มีขนาดอย่างน้อย 32 KB และสามารถใช้ memoryBarrierShared() เพื่อแสดง การเข้าถึงหน่วยความจำที่สอดคล้องกัน

กำหนดขนาดกลุ่มงาน

แม้ว่าพื้นที่ทำงานของปัญหาจะทำงานได้ดีกับขนาดกลุ่มงานขนาด 1 การตั้งค่า ขนาดกลุ่มงานที่เหมาะสมเป็นสิ่งสำคัญสำหรับการทำให้ตัวโหลดเงาประมวลผลขนานกัน หากขนาดเล็กเกินไป ไดรเวอร์ GPU อาจไม่ทำให้การประมวลผลขนานกัน มากพอ เป็นต้น ตามหลักการ ขนาดเหล่านี้ควรปรับแต่งตาม GPU ค่าเริ่มต้นที่สมเหตุสมผลทำงานได้ดีพอในอุปกรณ์ปัจจุบัน เช่น กลุ่มงาน ขนาด 8x8 ในข้อมูลโค้ดตัวปรับแสงเงา

มี GL_MAX_COMPUTE_WORK_GROUP_COUNT แต่ก็เพียงพอ ต้อง อย่างน้อย 65535 ในทั้ง 3 แกนตามข้อกำหนด

ส่งตัวปรับแสงเงา

ขั้นตอนสุดท้ายในการประมวลผลการคำนวณคือจ่ายตัวปรับแสงเงาโดยใช้ ของฟังก์ชันการจ่ายงาน เช่น glDispatchCompute ฟังก์ชันการมอบหมายงานมีหน้าที่ดังนี้ สำหรับการตั้งค่าจำนวนกลุ่มงานสำหรับแต่ละแกน

GLES31.glDispatchCompute(
  roundUp(inputImage.width, WORKGROUP_SIZE_X),
  roundUp(inputImage.height, WORKGROUP_SIZE_Y),
  1 // Z workgroup size. 1 == only one z level, which indicates a 2D kernel
)

หากต้องการส่งคืนค่า โปรดรอให้การดำเนินการประมวลผลเสร็จสิ้นก่อนโดยใช้ อุปสรรคด้านหน่วยความจำ:

GLES31.glMemoryBarrier(GLES31.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)

ในการเชื่อมโยงเคอร์เนลหลายรายการเข้าด้วยกัน (เช่น หากต้องการย้ายข้อมูลโค้ดโดยใช้ ScriptGroup) ให้สร้างและส่ง หลายโปรแกรมและซิงค์การเข้าถึงเอาต์พุตด้วยหน่วยความจำ อุปสรรค

ตัวอย่างแอป แสดงงานประมวลผล 2 งาน ได้แก่

  • การหมุนเวียน HUE: งานประมวลผลที่มีตัวปรับแสงเงาการประมวลผลเดียว โปรดดู GLSLImageProcessor::rotateHue สำหรับตัวอย่างโค้ด
  • เบลอ: งานประมวลผลที่ซับซ้อนขึ้นซึ่งทำงานประมวลผล 2 รายการตามลำดับ ตัวปรับแสงเงา ดูตัวอย่างโค้ดได้ที่ GLSLImageProcessor::blur

ดูข้อมูลเพิ่มเติมเกี่ยวกับอุปสรรคด้านหน่วยความจำได้ที่ การรับประกันระดับการเข้าถึง และ ตัวแปรที่แชร์ ที่ใช้เวลาเพียง 2 นาที