RenderScript เป็นเฟรมเวิร์กสำหรับทำงานที่ต้องใช้การประมวลผลสูง และมีประสิทธิภาพสูงใน Android RenderScript เป็นหลักในการใช้การประมวลผลข้อมูลแบบขนาน แม้ว่าจะเป็นแบบอนุกรม ภาระงานก็จะได้ประโยชน์ด้วยเช่นกัน รันไทม์ของ RenderScript โหลดพร้อมกัน ทำงานได้ในโปรเซสเซอร์ที่ใช้ได้ในอุปกรณ์ เช่น CPU และ GPU หลายแกน วิธีนี้ช่วยให้ ให้คุณมุ่งเน้นที่การแสดงอัลกอริทึม มากกว่าการกำหนดเวลาทำงาน RenderScript คือ มีประโยชน์อย่างยิ่งสำหรับแอปพลิเคชันที่ดำเนินการประมวลผลรูปภาพ ถ่ายภาพเชิงประมวลผล หรือ คอมพิวเตอร์วิทัศน์
ในการเริ่มต้นใช้ RenderScript มีแนวคิดหลัก 2 ประการที่คุณควรทำความเข้าใจ:
- ตัวภาษาเองก็เป็นภาษาที่ได้มาจาก C99 เพื่อใช้เขียนการประมวลผลประสิทธิภาพสูง โค้ด การเขียนเคอร์เนล RenderScript อธิบายถึง ในการเขียนเคอร์เนลการประมวลผล
- Control API ใช้เพื่อจัดการอายุการใช้งานของทรัพยากร RenderScript และ ซึ่งควบคุมการดำเนินการของเคอร์เนล โดยมีให้ใช้งานใน 3 ภาษา ได้แก่ Java, C++ ใน Android NDK และภาษาเคอร์เนลที่มาจาก C99 เอง การใช้ RenderScript จาก Java Code และ Single-Source RenderScript ถึงโมเดลแรกและอย่างที่ 3 ตัวเลือกตามลำดับ
การเขียนเคอร์เนล RenderScript
โดยปกติเคอร์เนล RenderScript จะอยู่ในไฟล์ .rs
ใน
ไดเรกทอรี <project_root>/src/rs
แต่ละไฟล์ของ .rs
จะเรียกว่า
script ทุกสคริปต์จะมีชุดเคอร์เนล ฟังก์ชัน และตัวแปรของตัวเอง สคริปต์สามารถ
ประกอบด้วย:
- การประกาศ Pragma (
#pragma version(1)
) ที่ประกาศเวอร์ชันของ ภาษาเคอร์เนลของ RenderScript ที่ใช้ในสคริปต์นี้ ปัจจุบัน 1 เป็นค่าเดียวที่ถูกต้อง - ประกาศของ Pragma (
#pragma rs java_package_name(com.example.app)
) ที่ ประกาศชื่อแพ็กเกจของคลาส Java ที่แสดงจากสคริปต์นี้ โปรดทราบว่าไฟล์.rs
ต้องเป็นส่วนหนึ่งของแพ็กเกจแอปพลิเคชัน ไม่ใช่ใน โครงการห้องสมุด - มีฟังก์ชันที่เรียกใช้ไม่ได้ 0 รายการหรือมากกว่า ฟังก์ชันที่เรียกใช้ได้คือ RenderScript แบบแยกชุดข้อความเดียว ที่คุณสามารถเรียกใช้จากโค้ด Java ด้วยอาร์กิวเมนต์ที่กำหนดเอง ซึ่งมักมีประโยชน์สำหรับ การตั้งค่าเริ่มต้นหรือการคำนวณแบบอนุกรมภายในไปป์ไลน์การประมวลผลขนาดใหญ่
ทั่วโลกของสคริปต์ 0 รายการขึ้นไป สคริปต์ส่วนกลางคล้ายกับตัวแปรร่วมใน C คุณสามารถ เข้าถึงโค้ดทั่วโลกของสคริปต์จากโค้ด Java ซึ่งมักใช้ในการส่งพารามิเตอร์ไปยัง RenderScript เคอร์เนล โปรดดูคำอธิบายทั่วโลกของสคริปต์อย่างละเอียดที่นี่
เคอร์เนลประมวลผล 0 ขึ้นไป เคอร์เนลการประมวลผลคือฟังก์ชัน หรือคอลเล็กชันฟังก์ชันที่คุณสามารถกำหนดรันไทม์ของ RenderScript ให้ทำงานพร้อมกัน ในคอลเล็กชันของข้อมูล การประมวลผลมี 2 ประเภท เคอร์เนล: เคอร์เนลการแมป (หรือที่เรียกว่าเคอร์เนล foreach) และ reduction เคอร์เนล
เคอร์เนลการแมปเป็นฟังก์ชันคู่ขนานที่ดำเนินการกับคอลเล็กชัน
Allocations
ของมิติข้อมูลเดียวกัน โดยค่าเริ่มต้น หนึ่งครั้งต่อทุกพิกัดในขนาดเหล่านั้น ซึ่งโดยทั่วไปแล้วจะใช้ เปลี่ยนคอลเล็กชันอินพุตAllocations
เป็น เอาต์พุตAllocation
หนึ่งElement
ที่ต่อไปนี้เป็นตัวอย่างของเคอร์เนลการแมปแบบง่าย
uchar4 RS_KERNEL invert(uchar4 in, uint32_t x, uint32_t y) { uchar4 out = in; out.r = 255 - in.r; out.g = 255 - in.g; out.b = 255 - in.b; return out; }
ในทุกๆ ด้าน ค่านี้เหมือนกับ C มาตรฐาน พร็อพเพอร์ตี้
RS_KERNEL
ที่ใช้กับ ต้นแบบของฟังก์ชันระบุว่าฟังก์ชันนั้นเป็นเคอร์เนลการแมป RenderScript แทนที่จะเป็น ฟังก์ชันที่เรียกใช้ได้ ระบบจะเติมอาร์กิวเมนต์in
โดยอัตโนมัติตาม อินพุตAllocation
ที่ส่งไปยังการเปิดเคอร์เนล อาร์กิวเมนต์x
และy
คือ ที่กล่าวถึงด้านล่าง ค่าที่แสดงผลจากเคอร์เนลคือ เขียนไปยังตำแหน่งที่เหมาะสมในเอาต์พุตAllocation
โดยอัตโนมัติ โดยค่าเริ่มต้น เคอร์เนลนี้จะทำงานกับอินพุตทั้งหมดAllocation
โดยมีการเรียกใช้ฟังก์ชันเคอร์เนล 1 ครั้งต่อElement
ในAllocation
เคอร์เนลการแมปอาจมีอินพุต
Allocations
อย่างน้อย 1 รายการ เอาต์พุตเดียวAllocation
หรือทั้ง 2 อย่าง การตรวจสอบรันไทม์ของ RenderScript เพื่อให้แน่ใจว่าการจัดสรรอินพุตและเอาต์พุตทั้งหมดเหมือนกัน รวมถึงอินพุตและเอาต์พุตElement
ประเภท การจัดสรรตรงกับต้นแบบของเคอร์เนล หากการตรวจสอบข้อใดข้อหนึ่งไม่ประสบความสำเร็จ RenderScript มีข้อยกเว้นหมายเหตุ: ก่อน Android 6.0 (API ระดับ 23) เคอร์เนลการแมปอาจ มีอินพุต
Allocation
มากกว่า 1 รายการหากคุณต้องการอินพุตหรือเอาต์พุต
Allocations
มากกว่า เคอร์เนลมี ออบเจ็กต์เหล่านั้นควรเชื่อมโยงกับทั่วโลกของสคริปต์rs_allocation
และเข้าถึงจากเคอร์เนลหรือฟังก์ชันที่เรียกใช้ได้ ผ่านrsGetElementAt_type()
หรือrsSetElementAt_type()
หมายเหตุ:
RS_KERNEL
เป็นมาโคร กำหนดโดยอัตโนมัติโดย RenderScript เพื่อความสะดวกของคุณ:#define RS_KERNEL __attribute__((kernel))
เคอร์เนลการลดคือชุดฟังก์ชันที่ทำงานกับคอลเล็กชันอินพุต
Allocations
ของมิติข้อมูลเดียวกัน โดยค่าเริ่มต้น ฟังก์ชัน accumulator จะทำงาน 1 ครั้งในทุกๆ พิกัดในขนาดเหล่านั้น โดยทั่วไปจะใช้ (แต่ไม่ใช่เฉพาะ) เพื่อ "ลด" CANNOT TRANSLATE คอลเล็กชันของอินพุตAllocations
ไปยังนี่คือตัวอย่างของการลด เคอร์เนลที่รวมกันเป็น
Elements
อินพุต:#pragma rs reduce(addint) accumulator(addintAccum) static void addintAccum(int *accum, int val) { *accum += val; }
เคอร์เนลการลดประกอบด้วยฟังก์ชันที่ผู้ใช้เขียนอย่างน้อย 1 ฟังก์ชัน
#pragma rs reduce
ใช้เพื่อกำหนดเคอร์เนลโดยการระบุชื่อ (คือaddint
ในตัวอย่างนี้) และชื่อและบทบาทของฟังก์ชันที่ทำให้ เพิ่มเคอร์เนล (ฟังก์ชันaccumulator
addintAccum
ใน ตัวอย่าง) ฟังก์ชันดังกล่าวทั้งหมดต้องเป็นstatic
เคอร์เนลการลดเสมอ ต้องการฟังก์ชันaccumulator
และอาจมีฟังก์ชันอื่นๆ ด้วย ที่คุณต้องการให้เคอร์เนลทำฟังก์ชัน Accumulator ของการลดเคอร์เนลจะต้องแสดงผล
void
และต้องมีอย่างน้อย 2 อาร์กิวเมนต์ อาร์กิวเมนต์แรก (ในตัวอย่างนี้accum
) เป็นตัวชี้ไปยัง รายการข้อมูล accumulator และรายการที่ 2 (ในตัวอย่างนี้คือval
) คือ ป้อนข้อมูลอัตโนมัติโดยอิงตามอินพุตที่Allocation
ที่ส่งไปยัง การเปิดตัวเคอร์เนล รายการข้อมูล Accumulator สร้างขึ้นโดยรันไทม์ของ RenderScript โดย ค่าเริ่มต้นจะมีค่าเริ่มต้นเป็น 0 โดยค่าเริ่มต้น เคอร์เนลนี้จะทำงานกับอินพุตทั้งหมดAllocation
โดยมีการดำเนินการ 1 ครั้งของฟังก์ชัน Accumulator ต่อElement
ในAllocation
โดย ค่าเริ่มต้น ค่าสุดท้ายของรายการข้อมูลสะสมจะถือว่าเป็นผลของ และส่งกลับไปยัง Java รันไทม์ของ RenderScript ตรวจสอบเพื่อให้แน่ใจว่าการจัดสรรอินพุตประเภทElement
ตรงกับฟังก์ชัน Accumulator ต้นแบบ; หากไม่ตรงกัน RenderScript จะแสดงข้อผิดพลาดเคอร์เนลการลดมีอินพุต
Allocations
อย่างน้อย 1 รายการ แต่ไม่มีเอาต์พุตAllocations
ดูคำอธิบายโดยละเอียดเพิ่มเติมเกี่ยวกับเคอร์เนลการลดได้ที่นี่
ระบบรองรับเคอร์เนลการลดใน Android 7.0 (API ระดับ 24) ขึ้นไป
ฟังก์ชันเคอร์เนลการแมปหรือฟังก์ชันการสะสมเคอร์เนลแบบลดทอนอาจเข้าถึงพิกัด ของการดำเนินการปัจจุบันโดยใช้อาร์กิวเมนต์พิเศษ
x
y
และz
ซึ่งต้องเป็นประเภทint
หรือuint32_t
คุณจะใช้อาร์กิวเมนต์เหล่านี้หรือไม่ก็ได้ฟังก์ชันเคอร์เนลการแมปหรือการสะสมเคอร์เนลการลด อาจใช้อาร์กิวเมนต์พิเศษที่เป็นตัวเลือก
context
ประเภท rs_kernel_context จำเป็นสำหรับกลุ่ม API รันไทม์ที่ใช้ค้นหา พร็อพเพอร์ตี้บางอย่างของการดำเนินการปัจจุบัน เช่น rsGetDimX (อาร์กิวเมนต์context
ใช้ได้ใน Android 6.0 (API ระดับ 23) ขึ้นไป)- ฟังก์ชัน
init()
ที่ไม่บังคับ ฟังก์ชันinit()
เป็นประเภทพิเศษของ ฟังก์ชันที่เรียกใช้ได้ซึ่ง RenderScript เรียกใช้เมื่อมีการสร้างอินสแตนซ์สคริปต์ครั้งแรก ซึ่งช่วยให้บางคน การคำนวณจะเกิดขึ้นโดยอัตโนมัติขณะสร้างสคริปต์ - โปรโตคอลและฟังก์ชันของสคริปต์แบบคงที่อย่างน้อย 1 รายการ สคริปต์แบบคงที่ส่วนกลางเทียบเท่ากับ
สคริปต์ส่วนกลาง เว้นแต่ว่าจะไม่สามารถเข้าถึงได้จากโค้ด Java ฟังก์ชันแบบคงที่คือ C มาตรฐาน
ที่สามารถเรียกจากเคอร์เนลหรือฟังก์ชันที่เรียกใช้ได้ในสคริปต์ แต่ไม่เปิดเผย
กับ Java API หากสคริปต์ส่วนกลางหรือฟังก์ชันไม่จำเป็นต้องเข้าถึงจากโค้ด Java
ขอแนะนำให้ประกาศเป็น
static
การตั้งค่าความแม่นยำของจุดลอยตัว
คุณควบคุมระดับความแม่นยำของจุดลอยตัวที่กำหนดในสคริปต์ได้ วิธีนี้มีประโยชน์หาก ไม่จำเป็นต้องระบุมาตรฐาน IEEE 754-2008 แบบเต็ม (ใช้โดยค่าเริ่มต้น) แนวทางปฏิบัติต่อไปนี้สามารถกำหนด ระดับความแม่นยำของจุดลอยตัวที่แตกต่างกัน:
#pragma rs_fp_full
(ค่าเริ่มต้นหากไม่ได้ระบุไว้): สำหรับแอปที่ต้องใช้ ความแม่นยำของจุดลอยตัวตามที่ระบุไว้ในมาตรฐาน IEEE 754-2008#pragma rs_fp_relaxed
: สำหรับแอปที่ไม่ต้องใช้ IEEE 754-2008 ที่เข้มงวด เป็นไปตามข้อกำหนดและใช้ความแม่นยำน้อยกว่า โหมดนี้จะเปิดใช้ Flush-to-0 สำหรับเดนอร์มและ โดยปัดเศษเป็นศูนย์#pragma rs_fp_imprecise
: สำหรับแอปที่ไม่ได้มีความแม่นยำสูง โหมดนี้จะเปิดใช้ทุกอย่างในrs_fp_relaxed
รวมถึง ดังต่อไปนี้:- การดำเนินการที่เป็น -0.0 จะส่งกลับ +0.0 แทนได้
- การดำเนินงานใน INF และ NAN ไม่ได้กำหนดไว้
แอปพลิเคชันส่วนใหญ่ใช้ rs_fp_relaxed
ได้โดยไม่มีผลข้างเคียงใดๆ อาจเป็นไปได้อย่างมาก
เป็นประโยชน์ในสถาปัตยกรรมบางอย่างเนื่องจากการเพิ่มประสิทธิภาพเพิ่มเติมใช้ได้เฉพาะกับแบบผ่อนคลาย
ความแม่นยำ (เช่น คำแนะนำเกี่ยวกับ CPU แบบ SIMD)
การเข้าถึง RenderScript API จาก Java
เมื่อพัฒนาแอปพลิเคชัน Android ที่ใช้ RenderScript คุณสามารถเข้าถึง API ของแอปพลิเคชันได้จาก Java ใน หนึ่งใน 2 วิธีต่อไปนี้
android.renderscript
- API ในแพ็กเกจคลาสนี้คือ พร้อมใช้งานบนอุปกรณ์ที่ใช้ Android 3.0 (API ระดับ 11) ขึ้นไปandroid.support.v8.renderscript
- API ในแพ็กเกจนี้คือ มีให้บริการผ่านฝ่ายสนับสนุน Library ซึ่งช่วยให้คุณใช้กับอุปกรณ์ที่ใช้ Android 2.3 (API ระดับ 9) และ สูงขึ้น
ข้อดีและข้อเสียมีดังนี้
- หากคุณใช้ Support Library API ส่วน RenderScript ของแอปพลิเคชันจะ
ใช้ได้กับอุปกรณ์ที่ใช้ Android 2.3 (API ระดับ 9) ขึ้นไป ไม่ว่า RenderScript หรือไม่ก็ตาม
ฟีเจอร์ที่คุณใช้ วิธีนี้ทำให้แอปพลิเคชันของคุณทำงานได้บนอุปกรณ์มากกว่าการที่คุณใช้
API แบบเนทีฟ (
android.renderscript
) - ฟีเจอร์ RenderScript บางอย่างไม่พร้อมใช้งานผ่าน Support Library API
- หากใช้ API ของ Support Library คุณจะได้รับ APK ขนาดใหญ่กว่า (อาจจะอย่างมาก)
หากคุณใช้ API แบบดั้งเดิม (
android.renderscript
)
การใช้ API ไลบรารีการสนับสนุน RenderScript
หากต้องการใช้ Support Library RenderScript API คุณต้องกำหนดค่าการพัฒนา ในการเข้าถึงข้อมูลเหล่านั้น ต้องใช้เครื่องมือ Android SDK ต่อไปนี้ในการใช้งาน API เหล่านี้
- เครื่องมือ Android SDK เวอร์ชัน 22.2 ขึ้นไป
- Android SDK Build-tools เวอร์ชัน 18.1.0 ขึ้นไป
โปรดทราบว่าตั้งแต่ Android SDK Build-tools 24.0.0 และ Android 2.2 (API ระดับ 8) ไม่ได้รับการสนับสนุนอีกต่อไป
คุณสามารถตรวจสอบและอัปเดตเวอร์ชันที่ติดตั้งของเครื่องมือเหล่านี้ใน Android SDK Manager
หากต้องการใช้ Support Library RenderScript API ให้ทำดังนี้
- ตรวจสอบว่าคุณได้ติดตั้ง Android SDK เวอร์ชันที่จำเป็นแล้ว
- อัปเดตการตั้งค่าสำหรับกระบวนการบิลด์ของ Android ให้รวมการตั้งค่า RenderScript ดังต่อไปนี้
- เปิดไฟล์
build.gradle
ในโฟลเดอร์แอปของโมดูลแอปพลิเคชัน - เพิ่มการตั้งค่า RenderScript ต่อไปนี้ไปยังไฟล์
ดึงดูด
android { compileSdkVersion 33 defaultConfig { minSdkVersion 9 targetSdkVersion 19 renderscriptTargetApi 18 renderscriptSupportModeEnabled true } }
Kotlin
android { compileSdkVersion(33) defaultConfig { minSdkVersion(9) targetSdkVersion(19) renderscriptTargetApi = 18 renderscriptSupportModeEnabled = true } }
การตั้งค่าที่แสดงด้านบนจะควบคุมลักษณะการทำงานเฉพาะในกระบวนการบิลด์ของ Android
renderscriptTargetApi
- ระบุเวอร์ชันไบต์โค้ดที่จะ ที่สร้างขึ้น เราขอแนะนําให้คุณตั้งค่านี้ให้เป็นระดับ API ต่ำสุดที่สามารถทำได้ ฟังก์ชันทั้งหมดที่คุณใช้และตั้งค่าrenderscriptSupportModeEnabled
ไปยังtrue
ค่าที่ถูกต้องสำหรับการตั้งค่านี้คือค่าที่เป็นจำนวนเต็มใดๆ จาก 11 เป็นระดับ API ที่เผยแพร่ล่าสุด หากเวอร์ชัน SDK ต่ำสุดของคุณ ที่ระบุในไฟล์ Manifest ของแอปพลิเคชันของคุณเป็นค่าอื่น ค่าดังกล่าว ถูกละเว้นและค่าเป้าหมายในไฟล์บิลด์เพื่อตั้งค่าต่ำสุด เวอร์ชันของ SDKrenderscriptSupportModeEnabled
- ระบุว่าระบบสร้าง ไบต์โค้ดควรกลับไปใช้เวอร์ชันที่เข้ากันได้หากอุปกรณ์ทำงานอยู่ on ไม่รองรับเวอร์ชันเป้าหมาย
- เปิดไฟล์
- ในคลาสแอปพลิเคชันที่ใช้ RenderScript ให้เพิ่มการนำเข้าสำหรับ Support Library
ชั้นเรียน:
Kotlin
import android.support.v8.renderscript.*
Java
import android.support.v8.renderscript.*;
การใช้ RenderScript จากโค้ด Java หรือ Kotlin
การใช้ RenderScript จากโค้ด Java หรือ Kotlin จะใช้คลาส API ที่อยู่ในโค้ด
android.renderscript
หรือแพ็กเกจ android.support.v8.renderscript
พบบ่อยที่สุด
แอปพลิเคชันจะมีรูปแบบการใช้งานพื้นฐานแบบเดียวกัน:
- เริ่มต้นบริบท RenderScript บริบท
RenderScript
ที่สร้างด้วยcreate(Context)
จะทำให้ใช้งาน RenderScript ได้และมอบฟังก์ชัน เพื่อควบคุมอายุการใช้งานของออบเจ็กต์ RenderScript ที่ตามมาทั้งหมด คุณควรพิจารณาบริบท เป็นการดำเนินการที่อาจใช้เวลานาน เนื่องจากอาจสร้างทรัพยากรใน ชิ้นส่วนฮาร์ดแวร์ ข้อความไม่ควรอยู่ในเส้นทางวิกฤติของแอปพลิเคชัน เท่าที่จะเป็นไปได้ โดยทั่วไปแล้ว แอปพลิเคชันจะมีบริบทใน RenderScript เพียง 1 บริบทต่อครั้งเท่านั้น - สร้าง
Allocation
อย่างน้อย 1 รายการเพื่อส่งไปยัง สคริปต์Allocation
คือออบเจ็กต์ RenderScript ที่มี พื้นที่เก็บข้อมูลในปริมาณคงที่ได้ เคอร์เนลในสคริปต์ใช้เวลาAllocation
ออบเจ็กต์เป็นอินพุตและเอาต์พุต และออบเจ็กต์Allocation
รายการได้ เข้าถึงในเคอร์เนลโดยใช้rsGetElementAt_type()
และrsSetElementAt_type()
เมื่อเชื่อมโยงเป็นทั่วโลกของสคริปต์ ออบเจ็กต์Allocation
รายการอนุญาตให้ส่งอาร์เรย์จากโค้ด Java ไปยัง RenderScript หรือกลับกัน โดยปกติแล้ว ออบเจ็กต์Allocation
รายการจะสร้างขึ้นโดยใช้createTyped()
หรือcreateFromBitmap()
- สร้างสคริปต์อะไรก็ได้ที่จำเป็น สคริปต์มี 2 ประเภท
ให้คุณทราบเมื่อใช้ RenderScript:
- ScriptC: สคริปต์เหล่านี้คือสคริปต์ที่กำหนดโดยผู้ใช้ตามที่อธิบายไว้ในการเขียนเคอร์เนล RenderScript ข้างต้น ทุกสคริปต์มีคลาส Java
แสดงโดยคอมไพเลอร์ RenderScript เพื่อให้เข้าถึงสคริปต์จากโค้ด Java ได้ง่าย
ชั้นเรียนนี้มีชื่อ
ScriptC_filename
ตัวอย่างเช่น ถ้าเคอร์เนลการแมป ข้างต้นอยู่ในinvert.rs
และมีบริบท RenderScript อยู่แล้วmRenderScript
โค้ด Java หรือ Kotlin ที่จะสร้างอินสแตนซ์สคริปต์จะมีลักษณะดังนี้Kotlin
val invert = ScriptC_invert(renderScript)
Java
ScriptC_invert invert = new ScriptC_invert(renderScript);
- ScriptIntrinsic: นี่คือเคอร์เนล RenderScript ในตัวสำหรับการทำงานทั่วไป
เช่น การเบลอแบบเกาส์เชียน (Gaussian Blur) คอนโวลูชัน (Convolution) และการผสมรูปภาพ สำหรับข้อมูลเพิ่มเติม โปรดดูคลาสย่อยของ
ScriptIntrinsic
- ScriptC: สคริปต์เหล่านี้คือสคริปต์ที่กำหนดโดยผู้ใช้ตามที่อธิบายไว้ในการเขียนเคอร์เนล RenderScript ข้างต้น ทุกสคริปต์มีคลาส Java
แสดงโดยคอมไพเลอร์ RenderScript เพื่อให้เข้าถึงสคริปต์จากโค้ด Java ได้ง่าย
ชั้นเรียนนี้มีชื่อ
- ป้อนข้อมูลการจัดสรร ระบบจะเติมการจัดสรรด้วยข้อมูลที่ว่างเปล่าเมื่อจัดสรร ยกเว้นการจัดสรรที่สร้างด้วย
createFromBitmap()
สร้างครั้งแรก หากต้องการป้อนข้อมูลการจัดสรร ให้ใช้ "สำเนา" แบบใดแบบหนึ่ง ในAllocation
"สำเนา" เมธอดคือแบบซิงโครนัส - ตั้งค่าส่วนกลางของสคริปต์ที่จำเป็น คุณสามารถตั้งค่าทั่วโลกได้โดยใช้เมธอดใน
คลาส
ScriptC_filename
รายการเดียวกันซึ่งมีชื่อว่าset_globalname
สำหรับ เช่น หากต้องการตั้งค่าตัวแปรint
ที่ชื่อthreshold
ให้ใช้เมธอด เมธอด Javaset_threshold(int)
; และหากต้องการตั้งค่า ตัวแปรrs_allocation
ชื่อlookup
ให้ใช้ Java วิธีการset_lookup(Allocation)
เมธอดset
เป็นแบบอะซิงโครนัส - เปิดใช้งานเคอร์เนลและฟังก์ชันที่เรียกใช้ได้
วิธีเปิดใช้งานเคอร์เนลที่กำหนดมีดังนี้ แสดงในคลาส
ScriptC_filename
เดียวกันด้วยเมธอดชื่อ วันที่forEach_mappingKernelName()
หรือreduce_reductionKernelName()
การเปิดตัวเหล่านี้เป็นแบบไม่พร้อมกัน ทั้งนี้ขึ้นอยู่กับอาร์กิวเมนต์ของเคอร์เนล ใช้การจัดสรรอย่างน้อย 1 รายการ ซึ่งทั้งหมดต้องมีมิติข้อมูลเดียวกัน โดยค่าเริ่มต้น แอตทริบิวต์ เคอร์เนลจะทำงานในทุกพิกัดในขนาดเหล่านั้น เพื่อเรียกใช้เคอร์เนลบนเซ็ตย่อยของพิกัดเหล่านั้น ส่งScript.LaunchOptions
ที่เหมาะสมเป็นอาร์กิวเมนต์สุดท้ายไปยังเมธอดforEach
หรือreduce
เปิดฟังก์ชันที่เรียกใช้ได้โดยใช้เมธอด
invoke_functionName
ปรากฏในชั้นเรียนScriptC_filename
เดียวกัน การเปิดตัวเหล่านี้เป็นแบบไม่พร้อมกัน - ดึงข้อมูลจากวัตถุ
Allocation
รายการ และออบเจ็กต์ javaFutureType หากต้องการ เข้าถึงข้อมูลจากAllocation
จากโค้ด Java คุณต้องคัดลอกข้อมูลนั้น กลับไปยัง Java โดยใช้ "copy" อย่างใดอย่างหนึ่ง ในAllocation
ในการรับผลลัพธ์ของเคอร์เนลการลด คุณต้องใช้เมธอดjavaFutureType.get()
"สำเนา" และget()
เมธอดเป็นแบบซิงโครนัส - แยกบริบท RenderScript ออก คุณสามารถทำลายบริบท RenderScript ได้
ด้วย
destroy()
หรือการอนุญาตบริบท RenderScript เป็นขยะที่รวบรวมมา การดำเนินการนี้จะทำให้มีการใช้ออบเจ็กต์ของออบเจ็กต์นั้นต่อไป ที่จะแสดงข้อยกเว้น
โมเดลการดำเนินการแบบไม่พร้อมกัน
forEach
, invoke
, reduce
ที่แสดง
และ set
เป็นเมธอดแบบไม่พร้อมกัน ซึ่งแต่ละเมธอดอาจกลับไปยัง Java ก่อนที่จะดำเนินการตาม
การดำเนินการที่ขอ อย่างไรก็ตาม การดำเนินการแต่ละรายการจะเรียงลำดับตามลำดับที่มีการเรียกใช้
ชั้นเรียน Allocation
มี "copy" ในการคัดลอกข้อมูล
และจากการจัดสรร "สำเนา" ของเมธอดเป็นแบบซิงโครนัส และได้รับการเรียงลำดับตาม
ของการทำงานแบบอะซิงโครนัสด้านบนที่สัมผัสกับการจัดสรรเดียวกัน
คลาส javaFutureType ที่สะท้อนระบุ
เมธอด get()
เพื่อหาผลลัพธ์ของการลด get()
คือ
แบบซิงโครนัส และเรียงลำดับตามการลดลง (ซึ่งเป็นอะซิงโครนัส)
RenderScript ต้นทางเดียว
Android 7.0 (API ระดับ 24) เปิดตัวฟีเจอร์การเขียนโปรแกรมใหม่ที่เรียกว่าแหล่งที่มาเดียว
RenderScript ซึ่งมีการเปิดใช้งานเคอร์เนลจากสคริปต์ในจุดที่กำหนด แทนที่จะเป็น
จาก Java ปัจจุบันวิธีการนี้จำกัดไว้สำหรับเคอร์เนลการแมปเท่านั้น ซึ่งเรียกง่ายๆ ว่า "เคอร์เนล"
ในส่วนนี้เพื่อความกระชับ ฟีเจอร์ใหม่นี้ยังรองรับการสร้างการจัดสรรประเภท
rs_allocation
จากภายในสคริปต์ ตอนนี้ คุณสามารถ
ใช้อัลกอริทึมทั้งหมดภายในสคริปต์เท่านั้น แม้ว่าจะต้องมีการเรียกใช้งานเคอร์เนลหลายครั้งก็ตาม
คุณจะได้ประโยชน์ 2 อย่าง คือ โค้ดที่อ่านง่ายขึ้นเพราะช่วยให้ติดตั้งใช้งานอัลกอริทึมได้อย่างต่อเนื่อง
ภาษาเดียว และเป็นโค้ดที่อาจจะเร็วกว่า เพราะมีการเปลี่ยนระหว่าง Java กับ
RenderScript เมื่อมีการเปิดตัวเคอร์เนลหลายรายการ
ใน RenderScript ของแหล่งที่มาเดียว คุณเขียนเคอร์เนลตามที่อธิบายไว้ใน
การเขียน Kernel ของ RenderScript จากนั้นคุณเขียนฟังก์ชันที่เรียกใช้ไม่ได้
rsForEach()
เพื่อเปิดใช้ API นั้นจะใช้ฟังก์ชันเคอร์เนลเป็น
ตามด้วยการจัดสรรอินพุตและเอาต์พุต API ที่คล้ายกัน
rsForEachWithOptions()
รับอาร์กิวเมนต์ประเภทเพิ่มเติม
rs_script_call_t
ซึ่งระบุชุดย่อยขององค์ประกอบจากอินพุตและ
การจัดสรรเอาต์พุตสำหรับฟังก์ชันเคอร์เนลที่จะประมวลผล
ในการเริ่มการคำนวณ RenderScript คุณจะเรียกใช้ฟังก์ชันที่เรียกใช้ได้จาก Java
ทำตามขั้นตอนในการใช้ RenderScript จากโค้ด Java
ในขั้นตอนเปิดใช้งานเคอร์เนลที่เหมาะสม ให้เรียกใช้
ฟังก์ชันที่เรียกใช้ได้โดยใช้ invoke_function_name()
ซึ่งจะเริ่มต้น
การประมวลผลทั้งหมด รวมถึงการเปิดใช้งาน Kernel
การจัดสรรมักจำเป็นต่อการบันทึกและ
ผลลัพธ์ระดับกลางจากการเปิดใช้งานเคอร์เนลหนึ่งไปยังอีกโปรแกรมหนึ่ง คุณสร้างแท็กได้โดยใช้
rsCreateAllocation() รูปแบบหนึ่งที่ใช้งานง่ายของ API ดังกล่าวคือ
rsCreateAllocation_<T><W>(…)
โดยที่ T คือประเภทข้อมูลสำหรับ
และ W คือความกว้างเวกเตอร์สำหรับองค์ประกอบ API มีขนาดต่างๆ
มิติข้อมูล X, Y และ Z เป็นอาร์กิวเมนต์ สำหรับการจัดสรรแบบ 1 มิติ หรือ 2 มิติ ขนาดสำหรับมิติข้อมูล Y หรือ Z สามารถ
ละเว้น เช่น rsCreateAllocation_uchar4(16384)
สร้างการจัดสรรแบบ 1 มิติของ
16384 องค์ประกอบ แต่ละรายการเป็นประเภท uchar4
ระบบจะจัดการการจัดสรรโดยอัตโนมัติ คุณ
ไม่จำเป็นต้องเปิดเผยหรือปล่อยว่างอย่างชัดแจ้ง แต่คุณสามารถโทรหา
rsClearObject(rs_allocation* alloc)
เพื่อแสดงว่าคุณไม่ต้องใช้แฮนเดิลแล้ว
alloc
ลงในการจัดสรรที่สำคัญ
เพื่อให้ระบบมีเวลาว่างจากทรัพยากรได้เร็วที่สุด
ส่วนการเขียนเคอร์เนล RenderScript มีตัวอย่าง
เคอร์เนลที่สลับรูปภาพ ตัวอย่างด้านล่างขยายการใช้เอฟเฟกต์มากกว่า 1 รายการกับรูปภาพ 1 ภาพ
การใช้ Single-Source RenderScript ซึ่งรวมเคอร์เนลอื่นคือ greyscale
ซึ่งเปลี่ยน
รูปภาพเป็นภาพขาวดำ ฟังก์ชันที่เรียกใช้ได้ process()
แล้วนำ 2 เคอร์เนลนี้ไปใช้
ต่อกันไปยังรูปภาพอินพุต และสร้างรูปภาพเอาต์พุต การจัดสรรสำหรับทั้งอินพุตและ
เอาต์พุตจะส่งเป็นอาร์กิวเมนต์ประเภท
rs_allocation
// File: singlesource.rs #pragma version(1) #pragma rs java_package_name(com.android.rssample) static const float4 weight = {0.299f, 0.587f, 0.114f, 0.0f}; uchar4 RS_KERNEL invert(uchar4 in, uint32_t x, uint32_t y) { uchar4 out = in; out.r = 255 - in.r; out.g = 255 - in.g; out.b = 255 - in.b; return out; } uchar4 RS_KERNEL greyscale(uchar4 in) { const float4 inF = rsUnpackColor8888(in); const float4 outF = (float4){ dot(inF, weight) }; return rsPackColorTo8888(outF); } void process(rs_allocation inputImage, rs_allocation outputImage) { const uint32_t imageWidth = rsAllocationGetDimX(inputImage); const uint32_t imageHeight = rsAllocationGetDimY(inputImage); rs_allocation tmp = rsCreateAllocation_uchar4(imageWidth, imageHeight); rsForEach(invert, inputImage, tmp); rsForEach(greyscale, tmp, outputImage); }
คุณเรียกใช้ฟังก์ชัน process()
จาก Java หรือ Kotlin ได้ดังนี้
Kotlin
val RS: RenderScript = RenderScript.create(context) val script = ScriptC_singlesource(RS) val inputAllocation: Allocation = Allocation.createFromBitmapResource( RS, resources, R.drawable.image ) val outputAllocation: Allocation = Allocation.createTyped( RS, inputAllocation.type, Allocation.USAGE_SCRIPT or Allocation.USAGE_IO_OUTPUT ) script.invoke_process(inputAllocation, outputAllocation)
Java
// File SingleSource.java RenderScript RS = RenderScript.create(context); ScriptC_singlesource script = new ScriptC_singlesource(RS); Allocation inputAllocation = Allocation.createFromBitmapResource( RS, getResources(), R.drawable.image); Allocation outputAllocation = Allocation.createTyped( RS, inputAllocation.getType(), Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_OUTPUT); script.invoke_process(inputAllocation, outputAllocation);
ตัวอย่างนี้แสดงวิธีใช้งานอัลกอริทึมที่เกี่ยวข้องกับการเปิดตัวเคอร์เนล 2 รายการโดยสมบูรณ์ ในตัวภาษา RenderScript เอง ไม่มีแหล่งที่มาเดียว ใน RenderScript คุณจะต้องเปิดใช้งานเคอร์เนลทั้ง 2 เคอร์เนลจากโค้ด Java โดยแยกการเปิดตัวเคอร์เนล ตั้งแต่การกำหนดเคอร์เนล ซึ่งทำให้เข้าใจอัลกอริทึมทั้งหมดได้ยากขึ้น ไม่ใช่แค่ โค้ด RenderScript แบบต้นทางเดียวที่อ่านง่ายขึ้น และยังช่วยลดการเปลี่ยน ระหว่าง Java และสคริปต์ในการเรียกใช้เคอร์เนล อัลกอริทึมแบบวนซ้ำบางรายการอาจเปิดใช้งานเคอร์เนล หลายร้อยครั้ง ทำให้ค่าใช้จ่ายในการดำเนินการดังกล่าวต้องเพิ่มสูงขึ้นมาก
สคริปต์ทั่วโลก
สคริปต์ส่วนกลางคือค่าปกติที่ไม่ใช่ static
ตัวแปรร่วมในไฟล์สคริปต์ (.rs
) สำหรับสคริปต์
ส่วนกลางที่ชื่อว่า var ตามที่ระบุไว้ใน
ไฟล์ filename.rs
จะมี
เมธอด get_var
แสดงใน
คลาส ScriptC_filename
ยกเว้นทั่วโลก
เท่ากับ const
นอกจากนี้จะมี
เมธอด set_var
สคริปต์ส่วนกลางที่กำหนดจะมีค่า 2 ค่าแยกกัน นั่นคือ Java และค่า script ค่าเหล่านี้มีลักษณะการทำงานดังต่อไปนี้
- หาก var มีค่าเริ่มต้นแบบคงที่ในสคริปต์ ระบุค่าเริ่มต้นของ var ทั้งใน Java และพารามิเตอร์ สคริปต์ หากไม่ใช่ ค่าเริ่มต้นนั้นจะเป็น 0
- เข้าถึง var ภายในสคริปต์อ่านและเขียน ค่าสคริปต์
- เมธอด
get_var
จะอ่านค่า Java - เมธอด
set_var
(หากมี) จะเขียนคำสั่ง ค่า Java ทันทีและเขียนค่าสคริปต์ แบบไม่พร้อมกัน
หมายเหตุ: หมายถึง ยกเว้นสำหรับ ตัวเริ่มต้นแบบคงที่ในสคริปต์ ค่าที่เขียนไปยังโกลบอล ภายในสคริปต์จะมองไม่เห็น Java
เคอร์เนลแบบลดความลึก
การลด (Reduction) เป็นกระบวนการรวมการรวบรวมข้อมูลเข้าไว้ด้วยกัน วิธีนี้เป็นการเขียนโปรแกรมแบบพื้นฐานที่มีประโยชน์ในการเขียนโปรแกรมควบคู่กัน โดยมีแอปพลิเคชันอย่าง ดังต่อไปนี้:
- คำนวณผลรวมหรือผลิตภัณฑ์จากข้อมูลทั้งหมด
- การดำเนินการเชิงตรรกะของการคำนวณ (
and
,or
,xor
) ข้อมูลทั้งหมด - การหาค่าต่ำสุดหรือสูงสุดในข้อมูล
- ค้นหาค่าเฉพาะหรือค้นหาพิกัดของค่าที่ระบุภายในข้อมูล
ใน Android 7.0 (API ระดับ 24) ขึ้นไป RenderScript รองรับเคอร์เนลการลดเพื่อให้ อัลกอริทึมการลดที่ผู้ใช้เขียนเองที่มีประสิทธิภาพ คุณเปิดเคอร์เนลการลดในอินพุตที่มีได้ 1, 2 หรือ 3 มิติข้อมูล
ตัวอย่างด้านบนจะแสดงเคอร์เนลการลด addint แบบง่าย
ต่อไปนี้คือเคอร์เนลการลด findMinAndMax ที่ซับซ้อนขึ้น
จะค้นหาตำแหน่งของค่า long
ต่ำสุดและสูงสุดในแอตทริบิวต์
Allocation
แบบ 1 มิติ:
#define LONG_MAX (long)((1UL << 63) - 1) #define LONG_MIN (long)(1UL << 63) #pragma rs reduce(findMinAndMax) \ initializer(fMMInit) accumulator(fMMAccumulator) \ combiner(fMMCombiner) outconverter(fMMOutConverter) // Either a value and the location where it was found, or INITVAL. typedef struct { long val; int idx; // -1 indicates INITVAL } IndexedVal; typedef struct { IndexedVal min, max; } MinAndMax; // In discussion below, this initial value { { LONG_MAX, -1 }, { LONG_MIN, -1 } } // is called INITVAL. static void fMMInit(MinAndMax *accum) { accum->min.val = LONG_MAX; accum->min.idx = -1; accum->max.val = LONG_MIN; accum->max.idx = -1; } //---------------------------------------------------------------------- // In describing the behavior of the accumulator and combiner functions, // it is helpful to describe hypothetical functions // IndexedVal min(IndexedVal a, IndexedVal b) // IndexedVal max(IndexedVal a, IndexedVal b) // MinAndMax minmax(MinAndMax a, MinAndMax b) // MinAndMax minmax(MinAndMax accum, IndexedVal val) // // The effect of // IndexedVal min(IndexedVal a, IndexedVal b) // is to return the IndexedVal from among the two arguments // whose val is lesser, except that when an IndexedVal // has a negative index, that IndexedVal is never less than // any other IndexedVal; therefore, if exactly one of the // two arguments has a negative index, the min is the other // argument. Like ordinary arithmetic min and max, this function // is commutative and associative; that is, // // min(A, B) == min(B, A) // commutative // min(A, min(B, C)) == min((A, B), C) // associative // // The effect of // IndexedVal max(IndexedVal a, IndexedVal b) // is analogous (greater . . . never greater than). // // Then there is // // MinAndMax minmax(MinAndMax a, MinAndMax b) { // return MinAndMax(min(a.min, b.min), max(a.max, b.max)); // } // // Like ordinary arithmetic min and max, the above function // is commutative and associative; that is: // // minmax(A, B) == minmax(B, A) // commutative // minmax(A, minmax(B, C)) == minmax((A, B), C) // associative // // Finally define // // MinAndMax minmax(MinAndMax accum, IndexedVal val) { // return minmax(accum, MinAndMax(val, val)); // } //---------------------------------------------------------------------- // This function can be explained as doing: // *accum = minmax(*accum, IndexedVal(in, x)) // // This function simply computes minimum and maximum values as if // INITVAL.min were greater than any other minimum value and // INITVAL.max were less than any other maximum value. Note that if // *accum is INITVAL, then this function sets // *accum = IndexedVal(in, x) // // After this function is called, both accum->min.idx and accum->max.idx // will have nonnegative values: // - x is always nonnegative, so if this function ever sets one of the // idx fields, it will set it to a nonnegative value // - if one of the idx fields is negative, then the corresponding // val field must be LONG_MAX or LONG_MIN, so the function will always // set both the val and idx fields static void fMMAccumulator(MinAndMax *accum, long in, int x) { IndexedVal me; me.val = in; me.idx = x; if (me.val <= accum->min.val) accum->min = me; if (me.val >= accum->max.val) accum->max = me; } // This function can be explained as doing: // *accum = minmax(*accum, *val) // // This function simply computes minimum and maximum values as if // INITVAL.min were greater than any other minimum value and // INITVAL.max were less than any other maximum value. Note that if // one of the two accumulator data items is INITVAL, then this // function sets *accum to the other one. static void fMMCombiner(MinAndMax *accum, const MinAndMax *val) { if ((accum->min.idx < 0) || (val->min.val < accum->min.val)) accum->min = val->min; if ((accum->max.idx < 0) || (val->max.val > accum->max.val)) accum->max = val->max; } static void fMMOutConverter(int2 *result, const MinAndMax *val) { result->x = val->min.idx; result->y = val->max.idx; }
หมายเหตุ: มีตัวอย่างการลดเสียงเพิ่มเติม เคอร์เนลที่นี่
ในการเรียกใช้เคอร์เนลการลด รันไทม์ของ RenderScript จะสร้าง 1 รายการขึ้นไป
ตัวแปรที่เรียกว่า ข้อมูล accumulator
เพื่อรักษาสถานะของกระบวนการลดขนาด รันไทม์ของ RenderScript
เลือกจำนวนรายการข้อมูลสะสมในลักษณะที่จะเพิ่มประสิทธิภาพสูงสุด ประเภท
ของรายการข้อมูล accumulator (accumType) จะกำหนดโดย accumulator ของเคอร์เนล
ฟังก์ชัน -- อาร์กิวเมนต์แรกสำหรับฟังก์ชันนั้นคือตัวชี้ไปยังข้อมูลสะสม
รายการ โดยค่าเริ่มต้น รายการข้อมูล Accumulator ทุกรายการจะเริ่มที่ 0 (เหมือนกับว่า
โดย memset
); อย่างไรก็ตาม คุณอาจเขียนฟังก์ชันเริ่มต้นเพื่อดำเนินการบางอย่าง
แตกต่างกัน
ตัวอย่าง: ใน addint
เคอร์เนล รายการข้อมูลสะสม (ประเภท int
) จะใช้ในการเพิ่มอินพุต
ไม่มีฟังก์ชันเริ่มต้น ดังนั้นรายการข้อมูลสะสมแต่ละรายการจะเริ่มการทำงานเป็น
ศูนย์
ตัวอย่าง: ใน
เคอร์เนล findMinAndMax ซึ่งเป็นรายการข้อมูล Accumulator
(ประเภท MinAndMax
) ใช้เพื่อติดตามค่าต่ำสุดและสูงสุด
ที่พบจนถึงตอนนี้ มีฟังก์ชันเริ่มต้นเพื่อตั้งค่าเหล่านี้เป็น LONG_MAX
และ
LONG_MIN
ตามลำดับ และตั้งค่าตำแหน่งของค่าเหล่านี้เป็น -1 ซึ่งบ่งชี้ว่า
ค่าต่างๆ ไม่ได้แสดงอยู่ในส่วน (ว่าง) ของข้อมูลที่ป้อน
ประมวลผลแล้ว
RenderScript เรียกใช้ฟังก์ชัน Accumulator เพียงครั้งเดียวสำหรับทุกพิกัดใน อินพุต โดยปกติแล้ว ฟังก์ชันควรอัปเดตรายการข้อมูล Accumulator บางอย่าง ตามข้อมูลที่ป้อน
ตัวอย่าง: ใน addint เคอร์เนล ฟังก์ชัน accumulator จะเพิ่มค่าขององค์ประกอบอินพุตไปยัง accumulator รายการข้อมูล
ตัวอย่าง: ใน เคอร์เนล findMinAndMax ซึ่งเป็นฟังก์ชัน accumulator ตรวจสอบว่าค่าขององค์ประกอบอินพุตน้อยกว่าหรือเท่ากับค่าต่ำสุด ค่าที่บันทึกในรายการข้อมูล Accumulator และ/หรือมากกว่าหรือเท่ากับค่าสูงสุด ค่าที่บันทึกในรายการข้อมูล accumulator และอัปเดตรายการข้อมูล accumulator ตามนั้น
หลังจากที่เรียกฟังก์ชัน Accumulator 1 ครั้งสำหรับทุกพิกัดในอินพุตแล้ว RenderScript ต้องรวม accumulator เข้าด้วยกัน รายการข้อมูลรวมกันเป็นรายการเดียว คุณสามารถเขียน combiner ฟังก์ชันเพื่อดำเนินการนี้ หากฟังก์ชัน Accumulator มีอินพุตเดียวและ ไม่มีอาร์กิวเมนต์พิเศษ คุณไม่จำเป็นต้องเขียนโปรแกรมผสม RenderScript จะใช้ฟังก์ชัน Accumulator เพื่อรวมข้อมูลของสะสม รายการ (คุณอาจเขียนฟังก์ชันคอมโพสิตได้ หากลักษณะการทำงานเริ่มต้นนี้ไม่ใช่สิ่งที่คุณ ต้องการ)
ตัวอย่าง: ใน addint เคอร์เนล ไม่มีฟังก์ชันคอมผสม ระบบจึงจะนำฟังก์ชัน accumulator มาใช้ นี่คือ พฤติกรรมที่ถูกต้อง เพราะหากเราแยกชุดของค่าออกเป็น 2 ส่วน และเรา บวกค่าใน 2 ส่วนนั้นแยกกัน การนำผลบวกทั้ง 2 มาบวกกันก็จะเหมือนกับ เพิ่มทั้งคอลเล็กชัน
ตัวอย่าง: ใน
เคอร์เนล findMinAndMax ฟังก์ชันการรวม
ตรวจสอบเพื่อดูว่าค่าต่ำสุดที่บันทึกไว้ใน "แหล่งที่มา" ข้อมูล Accumulator
รายการ *val
น้อยกว่าค่าต่ำสุดที่บันทึกไว้ใน "ปลายทาง"
รายการข้อมูลสะสม *accum
และอัปเดต*accum
ตามนั้น โดยจะทำงานที่คล้ายกันเพื่อให้ได้ค่าสูงสุด การอัปเดตนี้ในวันที่ *accum
กลับไปเป็นสถานะที่ควรจะได้ หากค่าอินพุตทั้งหมดมีการรวบรวมไว้ใน
*accum
แทนบางส่วนลงใน *accum
และบางส่วนเป็น
*val
หลังจากรวมรายการข้อมูล Accumulator ทั้งหมดแล้ว RenderScript จะระบุ ผลจากการลดการกลับไปใช้ Java คุณสามารถเขียน outconverter ฟังก์ชันเพื่อดำเนินการนี้ คุณไม่จำเป็นต้องเขียนฟังก์ชัน Outconverter หากคุณต้องการ ค่าสุดท้ายของรายการข้อมูลสะสมแบบรวมซึ่งเป็นผลของการลด
ตัวอย่าง: ในเคอร์เนล addint ระบบไม่มีฟังก์ชัน Outconverter ค่าสุดท้ายของรายการข้อมูลรวมคือผลรวมของ ทุกเอลิเมนต์ของอินพุต ซึ่งก็คือค่าที่เราต้องการแสดง
ตัวอย่าง: ใน
เคอร์เนล findMinAndMax ซึ่งเป็นฟังก์ชัน Outconverter
เริ่มต้นค่าผลลัพธ์ int2
เพื่อเก็บตำแหน่งของค่าขั้นต่ำและ
ค่าสูงสุดที่เกิดจากชุดค่าผสมของรายการข้อมูล accumulator ทั้งหมด
กำลังเขียนเคอร์เนลการลด
#pragma rs reduce
กำหนดเคอร์เนลการลดโดยใช้
โดยจะระบุชื่อ ชื่อ และบทบาทของฟังก์ชัน
เคอร์เนล ฟังก์ชันดังกล่าวทั้งหมดจะต้อง
static
เคอร์เนลการลดต้องใช้ accumulator
เสมอ
คุณจะสามารถข้ามฟังก์ชันอื่นๆ บางส่วนหรือทั้งหมดก็ได้ ขึ้นอยู่กับว่าคุณต้องการให้ฟังก์ชันใด
เคอร์เนล
#pragma rs reduce(kernelName) \ initializer(initializerName) \ accumulator(accumulatorName) \ combiner(combinerName) \ outconverter(outconverterName)
รายการต่างๆ ใน #pragma
มีความหมายดังนี้
reduce(kernelName)
(บังคับ): ระบุว่าเคอร์เนลการลดคือ มีการกำหนดไว้ Method ของ Java ที่สะท้อนreduce_kernelName
จะเปิดตัว เคอร์เนลinitializer(initializerName)
(ไม่บังคับ): ระบุชื่อของ ฟังก์ชันเริ่มต้นสำหรับเคอร์เนลการลดนี้ เมื่อคุณเปิดใช้งานเคอร์เนล จะเรียกใช้ RenderScript ฟังก์ชันนี้ 1 ครั้งสำหรับรายการข้อมูล Accumulator แต่ละรายการ ต้องกำหนดไว้ดังนี้static void initializerName(accumType *accum) { … }
accum
คือตัวชี้ไปยังรายการข้อมูลสะสมสำหรับฟังก์ชันนี้ เริ่มต้นหากคุณไม่ได้มีฟังก์ชันเริ่มต้น RenderScript จะเป็นผู้เริ่มต้น Accumulator ทั้งหมด รายการข้อมูลเป็น 0 (เหมือนกับภายใน
memset
) โดยทำงานเสมือนว่ามีการเริ่มต้น ที่มีหน้าตาดังนี้static void initializerName(accumType *accum) { memset(accum, 0, sizeof(*accum)); }
accumulator(accumulatorName)
(mandatory): ระบุชื่อฟังก์ชัน accumulator สำหรับ เคอร์เนลลดลง เมื่อคุณเปิดใช้งานเคอร์เนล จะเรียกใช้ RenderScript ฟังก์ชันนี้ครั้งเดียวต่อทุกพิกัดในอินพุต เพื่ออัปเดตแท็ก รายการข้อมูลสะสมในทางใดทางหนึ่งตามอินพุต ฟังก์ชัน ต้องมีคำจำกัดความดังนี้static void accumulatorName(accumType *accum, in1Type in1, …, inNType inN [, specialArguments]) { … }
accum
คือตัวชี้ไปยังรายการข้อมูลสะสมสำหรับฟังก์ชันนี้ แก้ไขin1
ถึงinN
คืออาร์กิวเมนต์อย่างน้อย 1 รายการที่ จะมีการเติมข้อมูลโดยอัตโนมัติตามอินพุตที่ส่งไปยังการเปิดใช้เคอร์เนล อาร์กิวเมนต์หนึ่งรายการ ต่ออินพุต ฟังก์ชัน Accumulator อาจใช้อาร์กิวเมนต์พิเศษใดก็ได้เคอร์เนลตัวอย่างที่มีหลายอินพุตคือ
dotProduct
combiner(combinerName)
(ไม่บังคับ): ระบุชื่อฟังก์ชันโปรแกรมรวมสำหรับฟังก์ชันนี้ เคอร์เนลลดลง หลังจาก RenderScript เรียกใช้ฟังก์ชัน Accumulator หนึ่งครั้งต่อทุกพิกัดในอินพุต ฟังก์ชันนี้จะเรียกใช้ฟังก์ชันนี้ จำนวนครั้งเท่าที่จำเป็นในการรวมรายการข้อมูลสะสมทั้งหมดเป็นรายการเดียว รายการข้อมูล Accumulator ฟังก์ชันต้องได้รับการกำหนดไว้ดังนี้
static void combinerName(accumType *accum, const accumType *other) { … }
accum
คือตัวชี้ไปยัง "จุดหมาย" รายการข้อมูล Accumulator สำหรับสิ่งนี้ เพื่อแก้ไขother
เป็นตัวชี้ไปยัง "แหล่งที่มา" รายการข้อมูล Accumulator เพื่อให้ฟังก์ชันนี้ "รวม" ลงใน*accum
หมายเหตุ: เป็นไปได้ เริ่มต้น
*accum
,*other
หรือทั้งคู่แล้ว แต่ยังไม่เคยดำเนินการ ที่ส่งไปยังฟังก์ชัน Accumulator กล่าวคือ ไม่เคยมีการอัปเดตรายการใดรายการหนึ่งหรือทั้งสองรายการ ตามข้อมูลที่ป้อน ตัวอย่างเช่น ใน เคอร์เนล findMinAndMax เครื่องมือผสาน ฟังก์ชันfMMCombiner
จะตรวจสอบidx < 0
อย่างชัดเจนเนื่องจาก ระบุรายการข้อมูลสะสม ซึ่งมีค่าเป็น INITVALหากคุณไม่ได้ระบุฟังก์ชันคำสั่ง RenderScript ใช้ฟังก์ชัน Accumulator ใน ซึ่งทำงานราวกับมีฟังก์ชันตัวรวมที่มีลักษณะดังนี้
static void combinerName(accumType *accum, const accumType *other) { accumulatorName(accum, *other); }
จำเป็นต้องใช้ฟังก์ชันคอมโพเนลหากเคอร์เนลมีอินพุตมากกว่า 1 รายการ (หากข้อมูลอินพุต) ชนิด ไม่เหมือนประเภทข้อมูล Accumulator หรือหากฟังก์ชัน Accumulator ใช้เวลาเพียง 1 ประเภท อาร์กิวเมนต์พิเศษขึ้นไป
outconverter(outconverterName)
(ไม่บังคับ): ระบุชื่อของฟังก์ชันเอาต์ตัวแปลงสำหรับพารามิเตอร์นี้ เคอร์เนลลดลง หลังจาก RenderScript รวม Accumulator ทั้งหมดแล้ว โมเดลจะเรียกฟังก์ชันนี้เพื่อระบุผลลัพธ์ของค่า เพื่อกลับไปยัง Java ฟังก์ชันดังกล่าวต้องมีการกำหนด เช่น ดังนี้static void outconverterName(resultType *result, const accumType *accum) { … }
result
คือตัวชี้ไปยังรายการข้อมูลผลลัพธ์ (จัดสรรแล้วแต่ยังไม่ได้เริ่มต้น โดยรันไทม์ของ RenderScript) เพื่อให้ฟังก์ชันนี้เริ่มทำงานด้วยผลลัพธ์ของ ที่ลดลง resultType คือประเภทของรายการข้อมูลนั้น ซึ่งไม่จำเป็นต้องเหมือนกัน เป็น accumTypeaccum
คือตัวชี้ไปยังรายการข้อมูล Accumulator สุดท้าย ที่คํานวณโดยฟังก์ชัน Combinerหากคุณไม่ได้มีฟังก์ชัน Outconverter ไว้ RenderScript จะคัดลอก Accumulator สุดท้าย ไปยังรายการข้อมูลผลลัพธ์ โดยทำงานราวกับมีฟังก์ชัน Outconverter ที่ ซึ่งมีลักษณะดังนี้
static void outconverterName(accumType *result, const accumType *accum) { *result = *accum; }
หากคุณต้องการผลลัพธ์ประเภทอื่นที่ไม่ใช่ประเภทข้อมูล Accumulator คุณต้องใช้ฟังก์ชัน Outconverter
โปรดทราบว่าเคอร์เนลจะมีประเภทอินพุต ประเภทข้อมูล Accumulator และประเภทผลลัพธ์
รายการใดที่ไม่จำเป็นต้องเหมือนกัน ตัวอย่างเช่น ใน
เคอร์เนล findMinAndMax อินพุต
ประเภท long
, ประเภทรายการข้อมูลสะสม MinAndMax
และผลลัพธ์
ประเภท int2
ไม่เหมือนกันเลย
อธิบายไม่ได้เลยว่าอย่างไร
คุณต้องไม่ต้องอาศัยจำนวนรายการข้อมูลสะสมที่ RenderScript สร้างขึ้นสำหรับ ที่ระบุการเปิดเคอร์เนล ไม่มีการรับประกันว่าการเปิดตัวเคอร์เนลเดียวกัน 2 ครั้งด้วย อินพุตเดียวกันจะสร้างรายการข้อมูลสะสมจำนวนเท่ากัน
คุณต้องไม่ต้องอาศัยลำดับที่ RenderScript เรียกโปรแกรมเริ่มต้น, accumulator และ ฟังก์ชันผสมผสาน หรืออาจเรียกทั้ง 2 อย่างนี้พร้อมกัน ไม่มีการรับประกันว่า การเปิดใช้งาน 2 เคอร์เนลเดียวกันที่มีอินพุตเดียวกันจะเป็นไปตามลำดับเดียวกัน มีเพียง รับประกันได้เพียงฟังก์ชันเริ่มต้นเท่านั้นที่เห็น Accumulator ที่ไม่ได้เริ่มต้น รายการข้อมูล เช่น
- ไม่มีการรับประกันว่ารายการข้อมูล Accumulator ทั้งหมดจะเริ่มต้นก่อน เรียกฟังก์ชัน Accumulator แม้ว่าจะเรียกใช้เฉพาะใน Accumulator ที่เริ่มต้นเท่านั้น รายการข้อมูล
- ไม่มีการรับประกันลำดับการส่งองค์ประกอบอินพุตไปยัง Accumulator
- ไม่มีการรับประกันว่ามีการเรียกฟังก์ชัน accumulator สำหรับองค์ประกอบอินพุตทั้งหมด ก่อนที่จะเรียกใช้ฟังก์ชันคอมโพสิต
ผลลัพธ์หนึ่งของเรื่องนี้คือแท็ก findMinAndMax เคอร์เนลไม่สามารถกำหนดได้: หากอินพุตมีรายการเดียวกันมากกว่า 1 รายการ ค่าต่ำสุดหรือสูงสุด คุณไม่มีทางทราบว่าเคอร์เนลจะ ค้นหา
คุณต้องรับประกันสิ่งใด
เนื่องจากระบบ RenderScript เลือกที่จะเรียกใช้เคอร์เนลในหลาย หลากหลายวิธี คุณต้องทำตามกฎบางอย่างเพื่อให้แน่ใจว่าเคอร์เนลทำงานตาม ในแบบที่คุณต้องการ หากไม่ปฏิบัติตามกฎเหล่านี้ คุณอาจได้รับผลลัพธ์ที่ไม่ถูกต้อง การทำงานที่ไม่กำหนดทิศทาง หรือข้อผิดพลาดเกี่ยวกับรันไทม์
กฎด้านล่างมักระบุว่ารายการข้อมูลสะสม 2 รายการต้องมี "องค์ประกอบ ค่าเดียวกัน" หมายความว่าอย่างไร ขึ้นอยู่กับสิ่งที่คุณต้องการให้เคอร์เนลทำ สำหรับ การลดทางคณิตศาสตร์ เช่น addint มักจะเหมาะสมกว่า สำหรับ "เดียวกัน" หมายถึงความเท่าเทียมทางคณิตศาสตร์ สำหรับ "เลือกอะไรก็ได้" ค้นหาแบบนั้น เป็น findMinAndMax ("ค้นหาตำแหน่งต่ำสุดและ ค่าอินพุตสูงสุด") ซึ่งอาจจะมีอินพุตที่เหมือนกันมากกว่า 1 รายการ ตำแหน่งทั้งหมดของค่าอินพุตที่ระบุต้องมีสถานะเป็น "เดียวกัน" คุณสามารถเขียนว่า เคอร์เนลที่คล้ายกันเพื่อ "หาตำแหน่งของค่าอินพุตต่ำสุดและสูงสุดซ้ายสุด" โดยที่ (เช่น) จะเลือกใช้ค่าขั้นต่ำที่ตำแหน่ง 100 มากกว่าค่าขั้นต่ำที่เหมือนกันที่สถานที่ตั้ง 200; สำหรับเคอร์เนลนี้ "เดียวกัน" จะหมายถึงตำแหน่งเดียวกัน ไม่ใช่แค่ value ที่เหมือนกัน และฟังก์ชัน Accumulator และคำสั่งรวมจะต้อง แตกต่างจากช่อง findMinAndMax
ฟังก์ชันเริ่มต้นต้องสร้างค่าข้อมูลประจำตัว นั่นคือ หากI
และ A
เป็นรายการข้อมูลสะสมเริ่มต้น
ด้วยฟังก์ชันเริ่มต้น และ I
ยังไม่เคยมีการส่งผ่านไปยัง
ฟังก์ชัน Accumulator (แต่อาจมี A
อยู่) จากนั้น
combinerName(&A, &I)
ต้อง ออกจากA
เหมือนเดิมcombinerName(&I, &A)
ต้อง ออกจากI
เดียวกันกับA
ตัวอย่าง: ใน addint เคอร์เนล รายการข้อมูลสะสมจะเริ่มต้นเป็น 0 ฟังก์ชันคอมโพสเซอร์ เคอร์เนลดำเนินการเพิ่ม 0 คือค่าอัตลักษณ์สำหรับการบวก
ตัวอย่าง: ใน findMinAndMax
เคอร์เนล รายการข้อมูลสะสมเริ่มต้นแล้ว
ถึง INITVAL
fMMCombiner(&A, &I)
ออกจากA
เหมือนเดิม เนื่องจากI
คือINITVAL
fMMCombiner(&I, &A)
เซ็ตI
เป็นA
เนื่องจากI
มีค่าเป็นINITVAL
ดังนั้น INITVAL
จึงเป็นค่าอัตลักษณ์
ฟังก์ชัน Joinr ต้อง commutative นั่นคือ
หาก A
และ B
เป็นรายการข้อมูลสะสมเริ่มต้น
ด้วยฟังก์ชันเริ่มต้น และอาจส่งผ่านไปยังฟังก์ชัน Accumulator เป็นศูนย์
หรือมากกว่า combinerName(&A, &B)
จะต้อง
ตั้งค่า A
เป็นค่าเดียวกัน
combinerName(&B, &A)
ชุด B
ตัวอย่าง: ใน addint เคอร์เนล ฟังก์ชันชุดค่าผสมจะเพิ่มค่ารายการข้อมูล Accumulator 2 ค่า การเพิ่มคือ แบบสลับที่กัน
ตัวอย่าง: ในเคอร์เนล findMinAndMax
fMMCombiner(&A, &B)
เหมือนกับ
A = minmax(A, B)
และ minmax
เป็นการสื่อสารระหว่างกัน ดังนั้น
fMMCombiner
ก็เช่นกัน
ฟังก์ชันเครื่องมือรวมต้องเป็นแบบเชื่อมโยง นั่นคือ
หาก A
, B
และ C
เป็น
รายการข้อมูล Accumulator ที่เริ่มต้นโดยฟังก์ชันเริ่มต้นและอาจส่งผ่าน
ลงในฟังก์ชัน Accumulator คูณด้วย 0 ขึ้นไป โค้ด 2 ลำดับต่อไปนี้จะต้อง
ตั้งค่า A
เป็นค่าเดียวกัน:
combinerName(&A, &B); combinerName(&A, &C);
combinerName(&B, &C); combinerName(&A, &B);
ตัวอย่าง: ในเคอร์เนล addint พารามิเตอร์ ฟังก์ชัน Joinr จะเพิ่มค่ารายการข้อมูล Accumulator 2 ค่าดังนี้
A = A + B A = A + C // Same as // A = (A + B) + C
B = B + C A = A + B // Same as // A = A + (B + C) // B = B + C
การเพิ่มมีความเชื่อมโยงกัน ดังนั้นฟังก์ชันคอมโพสิตจึงเชื่อมโยงด้วย
ตัวอย่าง: ในเคอร์เนล findMinAndMax
fMMCombiner(&A, &B)จะเหมือนกับ
A = minmax(A, B)ดังนั้น 2 ลำดับคือ
A = minmax(A, B) A = minmax(A, C) // Same as // A = minmax(minmax(A, B), C)
B = minmax(B, C) A = minmax(A, B) // Same as // A = minmax(A, minmax(B, C)) // B = minmax(B, C)
minmax
มีความเชื่อมโยง ดังนั้น fMMCombiner
จึงเป็นเช่นเดียวกัน
ฟังก์ชัน accumulator และฟังก์ชันชุดค่าผสมจะต้องทำตามหลักการพื้นฐาน
พับ กล่าวคือ หาก A
และ B
เป็นรายการข้อมูลสะสม A
ได้
เริ่มต้นโดยฟังก์ชันเริ่มต้นและอาจส่งผ่านไปยังฟังก์ชัน Accumulator
ศูนย์ครั้งหรือมากกว่า ไม่มีการเริ่มต้น B
และ อาร์กิวเมนต์
รายการอาร์กิวเมนต์อินพุตและอาร์กิวเมนต์พิเศษสำหรับการเรียก Accumulator ที่เฉพาะเจาะจง
ลำดับโค้ด 2 ชุดต่อไปนี้ต้องตั้งค่า A
เป็นค่าเดียวกัน:
accumulatorName(&A, args); // statement 1
initializerName(&B); // statement 2 accumulatorName(&B, args); // statement 3 combinerName(&A, &B); // statement 4
ตัวอย่าง: ในเคอร์เนล addint สําหรับค่าอินพุต V ให้ทําดังนี้
- ใบแจ้งยอด 1 เหมือนกับ
A += V
- ใบแจ้งยอด 2 เหมือนกับ
B = 0
- ใบแจ้งยอด 3 เหมือนกับ
B += V
ซึ่งเหมือนกับB = V
- ใบแจ้งยอด 4 เหมือนกับ
A += B
ซึ่งเหมือนกับA += V
คำสั่ง 1 และ 4 จะตั้งค่า A
เป็นค่าเดียวกัน และเคอร์เนลนี้ทำตามคำสั่ง
กฎการพับพื้นฐาน
ตัวอย่าง: ในเคอร์เนล findMinAndMax สำหรับอินพุต ค่า V ที่พิกัด X:
- ใบแจ้งยอด 1 เหมือนกับ
A = minmax(A, IndexedVal(V, X))
- ใบแจ้งยอด 2 เหมือนกับ
B = INITVAL
- ใบแจ้งยอด 3 เหมือนกับ
B = minmax(B, IndexedVal(V, X))
ซึ่งเนื่องจาก B คือค่าเริ่มต้น จะเหมือนกับB = IndexedVal(V, X)
- คำสั่ง 4 เหมือนกับ
A = minmax(A, B)
ซึ่งเหมือนกับ วันที่A = minmax(A, IndexedVal(V, X))
คำสั่ง 1 และ 4 จะตั้งค่า A
เป็นค่าเดียวกัน และเคอร์เนลนี้ทำตามคำสั่ง
กฎการพับพื้นฐาน
การเรียกใช้เคอร์เนลการลดจากโค้ด Java
สำหรับเคอร์เนลการลดชื่อ kernelName ที่กำหนดไว้ในฟิลด์
filename.rs
จะมี 3 วิธีที่แสดงใน
คลาส ScriptC_filename
:
Kotlin
// Function 1 fun reduce_kernelName(ain1: Allocation, …, ainN: Allocation): javaFutureType // Function 2 fun reduce_kernelName(ain1: Allocation, …, ainN: Allocation, sc: Script.LaunchOptions): javaFutureType // Function 3 fun reduce_kernelName(in1: Array<devecSiIn1Type>, …, inN: Array<devecSiInNType>): javaFutureType
Java
// Method 1 public javaFutureType reduce_kernelName(Allocation ain1, …, Allocation ainN); // Method 2 public javaFutureType reduce_kernelName(Allocation ain1, …, Allocation ainN, Script.LaunchOptions sc); // Method 3 public javaFutureType reduce_kernelName(devecSiIn1Type[] in1, …, devecSiInNType[] inN);
ต่อไปนี้เป็นตัวอย่างบางส่วนของการเรียกใช้เคอร์เนล addint
Kotlin
val script = ScriptC_example(renderScript) // 1D array // and obtain answer immediately val input1 = intArrayOf(…) val sum1: Int = script.reduce_addint(input1).get() // Method 3 // 2D allocation // and do some additional work before obtaining answer val typeBuilder = Type.Builder(RS, Element.I32(RS)).apply { setX(…) setY(…) } val input2: Allocation = Allocation.createTyped(RS, typeBuilder.create()).also { populateSomehow(it) // fill in input Allocation with data } val result2: ScriptC_example.result_int = script.reduce_addint(input2) // Method 1 doSomeAdditionalWork() // might run at same time as reduction val sum2: Int = result2.get()
Java
ScriptC_example script = new ScriptC_example(renderScript); // 1D array // and obtain answer immediately int input1[] = …; int sum1 = script.reduce_addint(input1).get(); // Method 3 // 2D allocation // and do some additional work before obtaining answer Type.Builder typeBuilder = new Type.Builder(RS, Element.I32(RS)); typeBuilder.setX(…); typeBuilder.setY(…); Allocation input2 = createTyped(RS, typeBuilder.create()); populateSomehow(input2); // fill in input Allocation with data ScriptC_example.result_int result2 = script.reduce_addint(input2); // Method 1 doSomeAdditionalWork(); // might run at same time as reduction int sum2 = result2.get();
วิธีที่ 1 มีอาร์กิวเมนต์ Allocation
อินพุต 1 รายการสำหรับ
ทุกอาร์กิวเมนต์อินพุตใน accumulator ของเคอร์เนล
Functions รันไทม์ของ RenderScript ตรวจสอบเพื่อให้แน่ใจว่าการจัดสรรอินพุตทั้งหมด
มีมิติข้อมูลเดียวกัน และ Element
ประเภทของแต่ละ
การจัดสรรอินพุตตรงกับอาร์กิวเมนต์อินพุตที่เกี่ยวข้องของ accumulator
ต้นแบบของฟังก์ชัน หากการตรวจสอบเหล่านี้ล้มเหลว RenderScript จะส่งข้อยกเว้น
เคอร์เนลจะทำงานในทุกพิกัดในขนาดเหล่านั้น
วิธีที่ 2 จะเหมือนกับวิธีที่ 1 เว้นแต่ว่าวิธีที่ 2 จะใช้เวลาเพิ่มเติม
อาร์กิวเมนต์ sc
ที่สามารถใช้เพื่อจำกัดการดำเนินการของเคอร์เนลกับส่วนย่อยของ
พิกัด
วิธีที่ 3 เหมือนกับวิธีที่ 1 เว้นแต่ว่า
แทนที่จะใช้อินพุตการจัดสรร
แต่จะต้องใช้อินพุตอาร์เรย์ Java ซึ่งเป็นการอำนวยความสะดวก
ช่วยคุณไม่ต้องเขียนโค้ดเพื่อสร้างการจัดสรรและคัดลอกข้อมูลอย่างชัดแจ้ง
จากอาร์เรย์ Java อย่างไรก็ตาม การใช้วิธีที่ 3 แทนวิธีที่ 1 ไม่ได้เพิ่มอัตรา
ประสิทธิภาพของโค้ด สำหรับอาร์เรย์อินพุตแต่ละรายการ วิธีที่ 3 จะสร้าง
การจัดสรรแบบ 1 มิติข้อมูลด้วยประเภท Element
ที่เหมาะสมและ
เปิดใช้ setAutoPadding(boolean)
แล้ว และคัดลอกอาร์เรย์ไปยัง
การจัดสรรเสมือนเป็นวิธีการที่เหมาะสมของ copyFrom()
ของ Allocation
จากนั้นจะเรียกเมธอด 1 ซึ่งส่งผ่านค่าชั่วคราว
การจัดสรร
หมายเหตุ: หากแอปพลิเคชันของคุณใช้การเรียกเคอร์เนลหลายครั้งด้วย อาร์เรย์เดียวกัน หรือที่มีมิติข้อมูลและประเภทขององค์ประกอบเดียวกันในอาร์เรย์ที่แตกต่างกัน คุณสามารถปรับปรุง โดยการสร้าง ป้อนข้อมูล และนำการจัดสรรซ้ำไปใช้ ด้วยตัวเอง แทนที่จะ โดยใช้วิธีที่ 3
javaFutureType
ประเภทการแสดงผลจากวิธีลดที่สะท้อนกลับแสดงถึง
คลาสที่ซ้อนกันแบบคงที่ภายใน ScriptC_filename
ซึ่งเป็นผลลัพธ์ในอนาคตของ
เคอร์เนล ในการรับผลลัพธ์ที่แท้จริงของการเรียกใช้ ให้เรียกใช้
เมธอด get()
ของคลาสนั้น ซึ่งจะแสดงค่า
ประเภท javaResultType get()
เป็นแบบซิงโครนัส
Kotlin
class ScriptC_filename(rs: RenderScript) : ScriptC(…) { object javaFutureType { fun get(): javaResultType { … } } }
Java
public class ScriptC_filename extends ScriptC { public static class javaFutureType { public javaResultType get() { … } } }
javaResultType ที่กำหนดจาก resultType ของ ฟังก์ชันเอาต์แปลง เว้นแต่ resultType จะเป็น ประเภทที่ไม่ได้ลงชื่อ (สเกลาร์ เวกเตอร์ หรืออาร์เรย์) javaResultType คือการจับคู่โดยตรง ประเภท Java หาก resultType เป็นประเภทที่ไม่มีการรับรองและมีประเภทที่มีการรับรองด้วย Java ที่ใหญ่กว่า javaResultType คือประเภทที่มีการรับรองด้วย Java ที่ใหญ่กว่า มิฉะนั้น จะเป็น ประเภท Java ที่สอดคล้องกัน เช่น
- หาก resultType คือ
int
,int2
หรือint[15]
แล้ว javaResultType คือint
,Int2
หรือint[]
ค่าของ resultType ทั้งหมดแสดงได้ ตาม javaResultType - หาก resultType คือ
uint
,uint2
หรือuint[15]
แล้ว javaResultType คือlong
,Long2
หรือlong[]
ค่าของ resultType ทั้งหมดแสดงได้ ตาม javaResultType - หาก resultType คือ
ulong
,ulong2
หรือulong[15]
แล้วกด javaResultType คือlong
,Long2
หรือlong[]
มีค่าบางอย่าง ของ resultType ที่แสดงด้วย javaResultType ไม่ได้
javaFutureType คือผลการค้นหาประเภทในอนาคตที่สอดคล้องกับ เป็น resultType ของ outconverter
- หาก resultType ไม่ใช่ประเภทอาร์เรย์ ระบบจะใช้ javaFutureType
มีค่าเป็น
result_resultType
- หาก resultType เป็นอาร์เรย์ของความยาว Count ที่มีสมาชิกประเภท memberType
javaFutureType จะเป็น
resultArrayCount_memberType
เช่น
Kotlin
class ScriptC_filename(rs: RenderScript) : ScriptC(…) { // for kernels with int result object result_int { fun get(): Int = … } // for kernels with int[10] result object resultArray10_int { fun get(): IntArray = … } // for kernels with int2 result // note that the Kotlin type name "Int2" is not the same as the script type name "int2" object result_int2 { fun get(): Int2 = … } // for kernels with int2[10] result // note that the Kotlin type name "Int2" is not the same as the script type name "int2" object resultArray10_int2 { fun get(): Array<Int2> = … } // for kernels with uint result // note that the Kotlin type "long" is a wider signed type than the unsigned script type "uint" object result_uint { fun get(): Long = … } // for kernels with uint[10] result // note that the Kotlin type "long" is a wider signed type than the unsigned script type "uint" object resultArray10_uint { fun get(): LongArray = … } // for kernels with uint2 result // note that the Kotlin type "Long2" is a wider signed type than the unsigned script type "uint2" object result_uint2 { fun get(): Long2 = … } // for kernels with uint2[10] result // note that the Kotlin type "Long2" is a wider signed type than the unsigned script type "uint2" object resultArray10_uint2 { fun get(): Array<Long2> = … } }
Java
public class ScriptC_filename extends ScriptC { // for kernels with int result public static class result_int { public int get() { … } } // for kernels with int[10] result public static class resultArray10_int { public int[] get() { … } } // for kernels with int2 result // note that the Java type name "Int2" is not the same as the script type name "int2" public static class result_int2 { public Int2 get() { … } } // for kernels with int2[10] result // note that the Java type name "Int2" is not the same as the script type name "int2" public static class resultArray10_int2 { public Int2[] get() { … } } // for kernels with uint result // note that the Java type "long" is a wider signed type than the unsigned script type "uint" public static class result_uint { public long get() { … } } // for kernels with uint[10] result // note that the Java type "long" is a wider signed type than the unsigned script type "uint" public static class resultArray10_uint { public long[] get() { … } } // for kernels with uint2 result // note that the Java type "Long2" is a wider signed type than the unsigned script type "uint2" public static class result_uint2 { public Long2 get() { … } } // for kernels with uint2[10] result // note that the Java type "Long2" is a wider signed type than the unsigned script type "uint2" public static class resultArray10_uint2 { public Long2[] get() { … } } }
หาก javaResultType เป็นประเภทออบเจ็กต์ (รวมถึงประเภทอาร์เรย์) การเรียกใช้แต่ละครั้ง
ไปยัง javaFutureType.get()
ในอินสแตนซ์เดียวกันจะส่งกลับ
ออบเจ็กต์
หาก javaResultType ไม่สามารถแสดงค่าของประเภท resultType ทั้งหมด และ
เคอร์เนลการลดทอนจะก่อให้เกิดค่าที่ไม่อาจนำเสนอได้
javaFutureType.get()
จะส่งกลับข้อยกเว้น
วิธีที่ 3 และ devecSiInXType
devecSiInXType เป็นประเภท Java ที่สอดคล้องกับ inXType ของอาร์กิวเมนต์ที่เกี่ยวข้องของ ฟังก์ชัน accumulator เว้นแต่ inXType จะเป็น ประเภทที่ไม่มีการรับรองหรือประเภทเวกเตอร์ devecSiInXType คือ Java ที่เกี่ยวข้องโดยตรง ประเภท ถ้า inXType เป็นประเภทสเกลาร์ที่ไม่มีการรับรอง devecSiInXType จะเป็นค่า ประเภท Java ที่สอดคล้องกับประเภทสเกลาร์ที่ลงชื่อ ขนาด หาก inXType เป็นประเภทเวกเตอร์ที่มีการรับรอง devecSiInXType จะเป็น Java ประเภทคอมโพเนนต์ของเวกเตอร์โดยตรง หาก inXType เป็นแบบไม่ลงชื่อเข้าใช้ ประเภทเวกเตอร์ devecSiInXType จะเป็นประเภท Java ที่สอดคล้องกับ ประเภทสเกลาร์แบบมีเครื่องหมายซึ่งมีขนาดเท่ากับประเภทคอมโพเนนต์ของเวกเตอร์ เช่น
- หาก inXType เป็น
int
ให้ devecSiInXType มีค่าเป็นint
- หาก inXType เป็น
int2
ให้ devecSiInXType มีค่าint
อาร์เรย์เป็นการนำเสนอแบบ flatten ซึ่งมีมากกว่า 2 เท่า องค์ประกอบสเกลาร์จำนวนมากเนื่องจากการจัดสรรมีเวกเตอร์ 2 องค์ประกอบ องค์ประกอบ ซึ่งจะทำงานแบบเดียวกับที่เมธอดcopyFrom()
ของAllocation
ทำงาน - หาก inXType เป็น
uint
ให้ใช้ deviceSiInXType มีค่าint
ค่าที่ลงชื่อในอาร์เรย์ Java จะถูกตีความเป็นค่าที่ไม่มีการลงนามของ บิตลายเดียวกันในการจัดสรร ซึ่งจะเป็นวิธีเดียวกับที่copyFrom()
วิธีการทำงานAllocation
- หาก inXType เป็น
uint2
ให้ใช้ deviceSiInXType มีค่าint
วิธีนี้เป็นการผสมผสานระหว่างint2
กับuint
มีการจัดการ: อาร์เรย์เป็นการแสดงค่าเดี่ยว และค่าที่มีการรับรองของอาร์เรย์ Java ถูกตีความเป็นค่า Element ที่ไม่มีการลงชื่อใน RenderScript
โปรดทราบว่าสำหรับวิธีที่ 3 ประเภทอินพุตจะมีการจัดการแตกต่างออกไป กว่าประเภทผลลัพธ์:
- อินพุตเวกเตอร์ของสคริปต์จะแบนด้าน Java ในขณะที่ผลลัพธ์เวกเตอร์ของสคริปต์จะไม่เป็นแบบราบ
- อินพุตที่ไม่มีการลงชื่อของสคริปต์จะแสดงเป็นอินพุตที่มีการลงชื่อที่มีขนาดเดียวกันใน Java
ในขณะที่ผลลัพธ์ที่ไม่ลงชื่อของสคริปต์จะแสดงเป็นประเภทที่มีการรับรองที่กว้างขึ้นใน Java
ด้านข้าง (ยกเว้นในกรณีของ
ulong
)
ตัวอย่างเพิ่มเติมของเคอร์เนลการลด
#pragma rs reduce(dotProduct) \ accumulator(dotProductAccum) combiner(dotProductSum) // Note: No initializer function -- therefore, // each accumulator data item is implicitly initialized to 0.0f. static void dotProductAccum(float *accum, float in1, float in2) { *accum += in1*in2; } // combiner function static void dotProductSum(float *accum, const float *val) { *accum += *val; }
// Find a zero Element in a 2D allocation; return (-1, -1) if none #pragma rs reduce(fz2) \ initializer(fz2Init) \ accumulator(fz2Accum) combiner(fz2Combine) static void fz2Init(int2 *accum) { accum->x = accum->y = -1; } static void fz2Accum(int2 *accum, int inVal, int x /* special arg */, int y /* special arg */) { if (inVal==0) { accum->x = x; accum->y = y; } } static void fz2Combine(int2 *accum, const int2 *accum2) { if (accum2->x >= 0) *accum = *accum2; }
// Note that this kernel returns an array to Java #pragma rs reduce(histogram) \ accumulator(hsgAccum) combiner(hsgCombine) #define BUCKETS 256 typedef uint32_t Histogram[BUCKETS]; // Note: No initializer function -- // therefore, each bucket is implicitly initialized to 0. static void hsgAccum(Histogram *h, uchar in) { ++(*h)[in]; } static void hsgCombine(Histogram *accum, const Histogram *addend) { for (int i = 0; i < BUCKETS; ++i) (*accum)[i] += (*addend)[i]; } // Determines the mode (most frequently occurring value), and returns // the value and the frequency. // // If multiple values have the same highest frequency, returns the lowest // of those values. // // Shares functions with the histogram reduction kernel. #pragma rs reduce(mode) \ accumulator(hsgAccum) combiner(hsgCombine) \ outconverter(modeOutConvert) static void modeOutConvert(int2 *result, const Histogram *h) { uint32_t mode = 0; for (int i = 1; i < BUCKETS; ++i) if ((*h)[i] > (*h)[mode]) mode = i; result->x = mode; result->y = (*h)[mode]; }
ตัวอย่างโค้ดเพิ่มเติม
BasicRenderScript RenderScriptIntrinsic และ Hello Compute ตัวอย่างเพิ่มเติมสาธิตการใช้ API ที่กล่าวถึงในหน้านี้