ภาพรวม RenderScript

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 ให้ทำดังนี้

  1. ตรวจสอบว่าคุณได้ติดตั้ง Android SDK เวอร์ชันที่จำเป็นแล้ว
  2. อัปเดตการตั้งค่าสำหรับกระบวนการบิลด์ของ 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 ของแอปพลิเคชันของคุณเป็นค่าอื่น ค่าดังกล่าว ถูกละเว้นและค่าเป้าหมายในไฟล์บิลด์เพื่อตั้งค่าต่ำสุด เวอร์ชันของ SDK
      • renderscriptSupportModeEnabled - ระบุว่าระบบสร้าง ไบต์โค้ดควรกลับไปใช้เวอร์ชันที่เข้ากันได้หากอุปกรณ์ทำงานอยู่ on ไม่รองรับเวอร์ชันเป้าหมาย
  3. ในคลาสแอปพลิเคชันที่ใช้ 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 พบบ่อยที่สุด แอปพลิเคชันจะมีรูปแบบการใช้งานพื้นฐานแบบเดียวกัน:

  1. เริ่มต้นบริบท RenderScript บริบท RenderScript ที่สร้างด้วย create(Context) จะทำให้ใช้งาน RenderScript ได้และมอบฟังก์ชัน เพื่อควบคุมอายุการใช้งานของออบเจ็กต์ RenderScript ที่ตามมาทั้งหมด คุณควรพิจารณาบริบท เป็นการดำเนินการที่อาจใช้เวลานาน เนื่องจากอาจสร้างทรัพยากรใน ชิ้นส่วนฮาร์ดแวร์ ข้อความไม่ควรอยู่ในเส้นทางวิกฤติของแอปพลิเคชัน เท่าที่จะเป็นไปได้ โดยทั่วไปแล้ว แอปพลิเคชันจะมีบริบทใน RenderScript เพียง 1 บริบทต่อครั้งเท่านั้น
  2. สร้าง Allocation อย่างน้อย 1 รายการเพื่อส่งไปยัง สคริปต์ Allocation คือออบเจ็กต์ RenderScript ที่มี พื้นที่เก็บข้อมูลในปริมาณคงที่ได้ เคอร์เนลในสคริปต์ใช้เวลา Allocation ออบเจ็กต์เป็นอินพุตและเอาต์พุต และออบเจ็กต์ Allocation รายการได้ เข้าถึงในเคอร์เนลโดยใช้ rsGetElementAt_type() และ rsSetElementAt_type() เมื่อเชื่อมโยงเป็นทั่วโลกของสคริปต์ ออบเจ็กต์ Allocation รายการอนุญาตให้ส่งอาร์เรย์จากโค้ด Java ไปยัง RenderScript หรือกลับกัน โดยปกติแล้ว ออบเจ็กต์ Allocation รายการจะสร้างขึ้นโดยใช้ createTyped()หรือcreateFromBitmap()
  3. สร้างสคริปต์อะไรก็ได้ที่จำเป็น สคริปต์มี 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
  4. ป้อนข้อมูลการจัดสรร ระบบจะเติมการจัดสรรด้วยข้อมูลที่ว่างเปล่าเมื่อจัดสรร ยกเว้นการจัดสรรที่สร้างด้วย createFromBitmap() สร้างครั้งแรก หากต้องการป้อนข้อมูลการจัดสรร ให้ใช้ "สำเนา" แบบใดแบบหนึ่ง ใน Allocation "สำเนา" เมธอดคือแบบซิงโครนัส
  5. ตั้งค่าส่วนกลางของสคริปต์ที่จำเป็น คุณสามารถตั้งค่าทั่วโลกได้โดยใช้เมธอดใน คลาส ScriptC_filename รายการเดียวกันซึ่งมีชื่อว่า set_globalname สำหรับ เช่น หากต้องการตั้งค่าตัวแปร int ที่ชื่อ threshold ให้ใช้เมธอด เมธอด Java set_threshold(int); และหากต้องการตั้งค่า ตัวแปร rs_allocation ชื่อ lookup ให้ใช้ Java วิธีการset_lookup(Allocation) เมธอด set เป็นแบบอะซิงโครนัส
  6. เปิดใช้งานเคอร์เนลและฟังก์ชันที่เรียกใช้ได้

    วิธีเปิดใช้งานเคอร์เนลที่กำหนดมีดังนี้ แสดงในคลาส ScriptC_filename เดียวกันด้วยเมธอดชื่อ วันที่ forEach_mappingKernelName() หรือ reduce_reductionKernelName() การเปิดตัวเหล่านี้เป็นแบบไม่พร้อมกัน ทั้งนี้ขึ้นอยู่กับอาร์กิวเมนต์ของเคอร์เนล ใช้การจัดสรรอย่างน้อย 1 รายการ ซึ่งทั้งหมดต้องมีมิติข้อมูลเดียวกัน โดยค่าเริ่มต้น แอตทริบิวต์ เคอร์เนลจะทำงานในทุกพิกัดในขนาดเหล่านั้น เพื่อเรียกใช้เคอร์เนลบนเซ็ตย่อยของพิกัดเหล่านั้น ส่ง Script.LaunchOptions ที่เหมาะสมเป็นอาร์กิวเมนต์สุดท้ายไปยังเมธอด forEach หรือ reduce

    เปิดฟังก์ชันที่เรียกใช้ได้โดยใช้เมธอด invoke_functionName ปรากฏในชั้นเรียน ScriptC_filename เดียวกัน การเปิดตัวเหล่านี้เป็นแบบไม่พร้อมกัน

  7. ดึงข้อมูลจากวัตถุ Allocation รายการ และออบเจ็กต์ javaFutureType หากต้องการ เข้าถึงข้อมูลจาก Allocation จากโค้ด Java คุณต้องคัดลอกข้อมูลนั้น กลับไปยัง Java โดยใช้ "copy" อย่างใดอย่างหนึ่ง ใน Allocation ในการรับผลลัพธ์ของเคอร์เนลการลด คุณต้องใช้เมธอด javaFutureType.get() "สำเนา" และ get() เมธอดเป็นแบบซิงโครนัส
  8. แยกบริบท 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 คือประเภทของรายการข้อมูลนั้น ซึ่งไม่จำเป็นต้องเหมือนกัน เป็น accumType accum คือตัวชี้ไปยังรายการข้อมูล 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 อยู่) จากนั้น

ตัวอย่าง: ใน 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 ที่กล่าวถึงในหน้านี้