RenderScript की खास जानकारी

RenderScript, Android पर बेहतर परफ़ॉर्मेंस के साथ, ज़्यादा कंप्यूटिंग वाले टास्क चलाने के लिए एक फ़्रेमवर्क है. RenderScript मुख्य रूप से डेटा-पैरलल कंप्यूटेशन के साथ इस्तेमाल करने के लिए बनाया गया है, हालांकि सीरियल वर्कलोड से भी फ़ायदा हो सकता है. RenderScript रनटाइम, डिवाइस पर उपलब्ध प्रोसेसर पर, एक साथ कई काम करता है. जैसे, मल्टी-कोर सीपीयू और जीपीयू. इससे आपको काम शेड्यूल करने के बजाय, एल्गोरिदम को बताने पर ध्यान दें. RenderScript, इमेज प्रोसेसिंग, कंप्यूटेशनल फ़ोटोग्राफ़ी या कंप्यूटर विज़न की सुविधा देने वाले ऐप्लिकेशन के लिए खास तौर पर मददगार है.

RenderScript के साथ शुरू करने के लिए, आपको दो मुख्य अवधारणाओं को समझना चाहिए:

  • भाषा, C99 से ली गई एक भाषा है. इसका इस्तेमाल, बेहतर परफ़ॉर्मेंस वाला कंप्यूट कोड लिखने के लिए किया जाता है. RenderScript कर्नेल लिखना कंप्यूट कर्नेल लिखने के लिए इसका इस्तेमाल कैसे करें.
  • कंट्रोल एपीआई का इस्तेमाल, RenderScript संसाधनों के लाइफ़टाइम को मैनेज करने और कर्नल के एक्सीक्यूशन को कंट्रोल करने के लिए किया जाता है. यह तीन अलग-अलग भाषाओं में उपलब्ध है: Java, Android NDK में C++, और C99 से ली गई कर्नेल भाषा. Java कोड से RenderScript का इस्तेमाल करना और सिंगल-सोर्स रेंडरस्क्रिप्ट में, पहली और तीसरे विकल्प भी हैं.

RenderScript कर्नेल लिखना

आम तौर पर, RenderScript केर्नेल <project_root>/src/rs डायरेक्ट्री में मौजूद .rs फ़ाइल में मौजूद होता है. हर .rs फ़ाइल को स्क्रिप्ट कहा जाता है. हर स्क्रिप्ट में, कर्नेल, फ़ंक्शन, और वैरिएबल का अपना सेट होता है. स्क्रिप्ट यह कर सकती है शामिल हैं:

  • प्राग्मा एलान (#pragma version(1)) जिसमें यह बताया जाता है कि इस स्क्रिप्ट में RenderScript कर्नेल भाषा का इस्तेमाल किया गया है. फ़िलहाल, सिर्फ़ 1 ही मान्य वैल्यू है.
  • एक प्रग्मा एलान (#pragma rs java_package_name(com.example.app)) जो इस स्क्रिप्ट से दिखाई गई Java क्लास के पैकेज का नाम बताता है. ध्यान दें कि आपकी .rs फ़ाइल, आपके ऐप्लिकेशन पैकेज का हिस्सा होनी चाहिए, न कि किसी लाइब्रेरी प्रोजेक्ट का.
  • शून्य या उससे ज़्यादा इंवोक किए जा सकने वाले फ़ंक्शन. कॉल किया जा सकने वाला फ़ंक्शन, एक थ्रेड वाला RenderScript फ़ंक्शन होता है. इसे अपने Java कोड से, मनमुताबिक आर्ग्युमेंट के साथ कॉल किया जा सकता है. ये अक्सर बड़ी प्रोसेसिंग पाइपलाइन में, शुरुआती सेटअप या सीरियल कैलकुलेशन के लिए काम के होते हैं.
  • शून्य या उससे ज़्यादा स्क्रिप्ट ग्लोबल. स्क्रिप्ट ग्लोबल, C में ग्लोबल वैरिएबल जैसा ही होता है. Java कोड से स्क्रिप्ट ग्लोबल को ऐक्सेस किया जा सकता है. आम तौर पर, इनका इस्तेमाल RenderScript केर्नेल में पैरामीटर पास करने के लिए किया जाता है. स्क्रिप्ट ग्लोबल के बारे में ज़्यादा जानकारी यहां दी गई है.

  • शून्य या उससे ज़्यादा कंप्यूट कर्नेल. कंप्यूट कर्नेल, एक फ़ंक्शन या फ़ंक्शन का कलेक्शन होता है. इसकी मदद से, RenderScript रनटाइम को डेटा के कलेक्शन में एक साथ कई काम करने के लिए निर्देश दिया जा सकता है. दो तरह की कंप्यूटिंग कर्नेल: मैपिंग कर्नेल (इन्हें foreach कर्नेल भी कहा जाता है) और रिडक्शन कर्नेल.

    मैपिंग कर्नेल एक पैरलल फ़ंक्शन है, जो एक जैसे डाइमेंशन के 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, Allocation में हर Element के लिए कर्नेल फ़ंक्शन के एक निष्पादन के साथ.

      मैपिंग केर्नेल में एक या उससे ज़्यादा इनपुट Allocations, एक आउटपुट Allocation या दोनों हो सकते हैं. RenderScript रनटाइम यह पक्का करने के लिए जांच करता है कि सभी इनपुट और आउटपुट ऐलोकेशन के डाइमेंशन एक जैसे हों. साथ ही, यह भी पक्का करता है कि इनपुट और आउटपुट ऐलोकेशन के Element टाइप, कर्नेल के प्रोटोटाइप से मेल खाते हों. अगर इनमें से कोई भी जांच पूरी नहीं होती है, तो RenderScript एक अपवाद दिखाता है.

      ध्यान दें: Android 6.0 (एपीआई लेवल 23) से पहले, मैपिंग कर्नेल में एक से ज़्यादा इनपुट Allocation नहीं हो सकते.

      अगर आपको कोर में मौजूद Allocations से ज़्यादा इनपुट या आउटपुट Allocations चाहिए, तो उन ऑब्जेक्ट को rs_allocation स्क्रिप्ट ग्लोबल के साथ बाउंड किया जाना चाहिए. साथ ही, rsGetElementAt_type() या rsSetElementAt_type() के ज़रिए, कोर या किसी ऐसे फ़ंक्शन से ऐक्सेस किया जाना चाहिए जिसे कॉल किया जा सकता है.

      ध्यान दें: RS_KERNEL एक मैक्रो है आपकी सुविधा के लिए RenderScript द्वारा अपने आप परिभाषित किया गया है:

      #define RS_KERNEL __attribute__((kernel))

    रिडक्शन कर्नेल, फ़ंक्शन का एक परिवार है जो इनपुट के कलेक्शन पर काम करता है एक जैसे डाइमेंशन का Allocations. डिफ़ॉल्ट रूप से, उन डाइमेंशन में मौजूद हर निर्देशांक के लिए, इसका accumulator फ़ंक्शन एक बार काम करता है. आम तौर पर, इसका इस्तेमाल "कम करने" के लिए किया जाता है. हालांकि, खास तौर पर इसका इस्तेमाल नहीं किया जाता एक सिंगल के लिए इनपुट Allocations का कलेक्शन वैल्यू.

    • यहां एक आसान कम करने वाले केर्नल का उदाहरण दिया गया है, जो अपने इनपुट के Elements को जोड़ता है:

      #pragma rs reduce(addint) accumulator(addintAccum)
      
      static void addintAccum(int *accum, int val) {
        *accum += val;
      }

      रिडक्शन कर्नेल में एक या उससे ज़्यादा उपयोगकर्ता के लिखे गए फ़ंक्शन होते हैं. #pragma rs reduce का इस्तेमाल, कर्नेल का नाम तय करके उसे तय करने के लिए किया जाता है (इस उदाहरण में addint) और बनाने वाले फ़ंक्शन के नाम और भूमिकाएं कर्नेल को ऊपर करें (इसमें accumulator फ़ंक्शन addintAccum है, इसमें उदाहरण के लिए). ऐसे सभी फ़ंक्शन static होने चाहिए. रिडक्शन कर्नेल के लिए, accumulator फ़ंक्शन का होना ज़रूरी है. इसमें अन्य फ़ंक्शन भी हो सकते हैं. यह इस बात पर निर्भर करता है कि आपको कर्नेल से क्या करना है.

      रिडक्शन कर्नेल अक्युमिलेटर फ़ंक्शन में void दिखना चाहिए. साथ ही, इसमें कम से कम यह फ़ंक्शन होना चाहिए दो आर्ग्युमेंट. पहला आर्ग्युमेंट (इस उदाहरण में accum), accumulator डेटा आइटम का पॉइंटर होता है. दूसरा आर्ग्युमेंट (इस उदाहरण में val), kernel लॉन्च के लिए पास किए गए इनपुट Allocation के आधार पर अपने-आप भर जाता है. Accumulator डेटा आइटम, RenderScript रनटाइम से बनाया जाता है. डिफ़ॉल्ट रूप से, इसे शून्य पर शुरू किया जाता है. डिफ़ॉल्ट रूप से, यह कर्नेल अपने पूरे इनपुटAllocation पर चलता है. साथ ही, Allocation में हर Element के लिए एक इकट्ठा करने वाले फ़ंक्शन को एक बार चलाया जाता है. इन्होंने बदलाव किया है डिफ़ॉल्ट, अक्युमिलेटर डेटा आइटम की आखिरी वैल्यू को कम कर देता है और उसे Java में वापस भेज दिया जाता है. RenderScript रनटाइम जांच करके यह पक्का करता है कि इनपुट ऐलोकेशन का Element टाइप, अक्यूम्युलेटर फ़ंक्शन से मेल खाता है प्रोटोटाइप; अगर यह मेल नहीं खाता है, तो रेंडर स्क्रिप्ट एक अपवाद दिखाता है.

      रिडक्शन कर्नेल में एक या उससे ज़्यादा इनपुट Allocations होते हैं, लेकिन कोई आउटपुट Allocations नहीं होता.

      रिडक्शन कर्नेल के बारे में यहां ज़्यादा जानकारी दी गई है.

      कम करने वाले कर्नेल Android 7.0 (एपीआई लेवल 24) और उसके बाद के वर्शन पर काम करते हैं.

    मैपिंग कर्नेल फ़ंक्शन या रिडक्शन कर्नेल अक्युमिलेटर फ़ंक्शन, निर्देशांकों को ऐक्सेस कर सकते हैं प्रोग्राम चलाने के लिए, खास आर्ग्युमेंट x का इस्तेमाल करें, y और z, जो int या uint32_t टाइप के होने चाहिए. ये आर्ग्युमेंट ज़रूरी नहीं हैं.

    मैपिंग केर्नेल फ़ंक्शन या रिडक्शन केर्नेल इक्यूमुलेटर फ़ंक्शन में, rs_kernel_context टाइप का वैकल्पिक खास आर्ग्युमेंट context भी लिया जा सकता है. क्वेरी करने के लिए इस्तेमाल किए जाने वाले रनटाइम एपीआई फ़ैमिली ग्रुप को इसकी ज़रूरत होती है मौजूदा एक्ज़ीक्यूशन की कुछ प्रॉपर्टी -- उदाहरण के लिए, rsGetDimX. (context आर्ग्युमेंट, Android 6.0 (एपीआई लेवल 23) और उसके बाद के वर्शन पर उपलब्ध है.)

  • एक वैकल्पिक init() फ़ंक्शन. init() फ़ंक्शन, एक खास तरह का ऐसा फ़ंक्शन है जिसे इस्तेमाल किया जा सकता है. यह फ़ंक्शन, स्क्रिप्ट को पहली बार इंस्टैंशिएट करने पर RenderScript को चलाता है. इससे कुछ लोगों को कंप्यूटेशन की मदद से, स्क्रिप्ट बनाते समय अपने-आप हो जाता है.
  • शून्य या उससे ज़्यादा स्टैटिक स्क्रिप्ट ग्लोबल और फ़ंक्शन. स्टैटिक स्क्रिप्ट ग्लोबल स्क्रिप्ट ग्लोबल, लेकिन Java कोड से इसे ऐक्सेस नहीं किया जा सकता. स्टैटिक फ़ंक्शन, स्टैंडर्ड C होता है फ़ंक्शन जिसे स्क्रिप्ट में किसी भी कर्नेल या इनवोकेबल फ़ंक्शन से कॉल किया जा सकता है, लेकिन वह दिखाया नहीं जाता को डाउनलोड करने में मदद मिलती है. अगर किसी स्क्रिप्ट ग्लोबल या फ़ंक्शन को Java कोड से ऐक्सेस करने की ज़रूरत नहीं है, तो हमारा सुझाव है कि उसे static के तौर पर घोषित करें.

फ़्लोटिंग पॉइंट की सटीक वैल्यू सेट करना

स्क्रिप्ट में फ़्लोटिंग पॉइंट के सटीक होने के लेवल को कंट्रोल किया जा सकता है. यह तब काम आता है, जब पूर्ण IEEE 754-2008 मानक (डिफ़ॉल्ट रूप से इस्तेमाल किया गया) की ज़रूरत नहीं है. यहां दिए गए प्रैगमा, फ़्लोटिंग पॉइंट की सटीक जानकारी का अलग लेवल सेट कर सकते हैं:

  • #pragma rs_fp_full (अगर कुछ न बताया गया हो, तो डिफ़ॉल्ट तौर पर): उन ऐप्लिकेशन के लिए जिनके लिए आईईईई 754-2008 स्टैंडर्ड में बताए गए फ़्लोटिंग पॉइंट के सटीक होने की पुष्टि करता है.
  • #pragma rs_fp_relaxed: उन ऐप्लिकेशन के लिए जिन्हें IEEE 754-2008 के सख्त दिशा-निर्देशों का पालन करने की ज़रूरत नहीं है और जिन्हें सटीक नतीजे देने की ज़रूरत नहीं है. यह मोड, डीनॉर्म और राउंड-टू-ज़ीरो के लिए, शून्य पर फ़्लश करने की सुविधा चालू करता है.
  • #pragma rs_fp_imprecise: ऐसे ऐप्लिकेशन के लिए जिनमें सटीक जानकारी नहीं होती ज़रूरतें. इस मोड से, rs_fp_relaxed में सभी चीज़ों को चालू किया जा सकता है. इसके अलावा, फ़ॉलो किया जा रहा है:
    • जिन संक्रियाओं के नतीजे -0.0 के रूप में मिलते हैं, वे इसके बजाय +0.0 दिखा सकते हैं.
    • INF और NAN पर ऑपरेशन की जानकारी नहीं दी गई है.

ज़्यादातर ऐप्लिकेशन rs_fp_relaxed का इस्तेमाल बिना किसी खराब असर के कर सकते हैं. यह बहुत कुछ आर्किटेक्चर के लिए फ़ायदेमंद है. इसकी वजह यह है कि अतिरिक्त ऑप्टिमाइज़ेशन, आरामदेह सटीक जानकारी (जैसे, SIMD सीपीयू के निर्देश).

Java से RenderScript API ऐक्सेस करना

RenderScript का इस्तेमाल करने वाले Android ऐप्लिकेशन को डेवलप करते समय, आप Java से इसके API को इसमें ऐक्सेस कर सकते हैं: दो तरीकों में से कोई एक:

ये रहे कुछ बदलाव:

  • अगर Support Library API का इस्तेमाल किया जाता है, तो आपके ऐप्लिकेशन का RenderScript हिस्सा, Android 2.3 (एपीआई लेवल 9) और उसके बाद के वर्शन वाले डिवाइसों पर काम करेगा. भले ही, आपने RenderScript की कौनसी सुविधाओं का इस्तेमाल किया हो. इससे आपका ऐप्लिकेशन, नेटिव (android.renderscript) एपीआई.
  • RenderScript की कुछ सुविधाएं, Support Library API के ज़रिए उपलब्ध नहीं हैं.
  • अगर नेटिव (android.renderscript) एपीआई का इस्तेमाल करने के बजाय, सहायता लाइब्रेरी एपीआई का इस्तेमाल किया जाता है, तो आपको APKs का साइज़ ज़्यादा (शायद काफ़ी ज़्यादा) मिलेगा.

RenderScript सहायता लाइब्रेरी के एपीआई का इस्तेमाल करना

Support Library RenderScript API का इस्तेमाल करने के लिए, आपको अपने डेवलपमेंट को कॉन्फ़िगर करना होगा को ऐक्सेस करने की क्षमता नहीं होती. Android SDK के इन टूल का इस्तेमाल करने की ज़रूरत है ये एपीआई:

  • Android SDK टूल का वर्शन 22.2 या इसके बाद वाला वर्शन
  • Android SDK बिल्ड-टूल रिविज़न 18.1.0 या इसके बाद वाला वर्शन

ध्यान दें कि Android SDK बिल्ड-टूल 24.0.0 और Android 2.2 से शुरू करके (एपीआई लेवल 8) अब काम नहीं करता.

Android SDK मैनेजर में जाकर, इन टूल के इंस्टॉल किए गए वर्शन की जांच की जा सकती है और उन्हें अपडेट किया जा सकता है.

सपोर्ट लाइब्रेरी RenderScript API का इस्तेमाल करने के लिए:

  1. पक्का करें कि आपने Android SDK का ज़रूरी वर्शन इंस्टॉल किया हो.
  2. RenderScript सेटिंग शामिल करने के लिए, Android बिल्ड प्रोसेस की सेटिंग अपडेट करें:
    • build.gradle फ़ाइल को अपने ऐप्लिकेशन मॉड्यूल के ऐप्लिकेशन फ़ोल्डर में खोलें.
    • फ़ाइल में ये RenderScript सेटिंग जोड़ें:
      GroovyKotlin
              android {
                  compileSdkVersion 33
      
                  defaultConfig {
                      minSdkVersion 9
                      targetSdkVersion 19
      
                      renderscriptTargetApi 18
                      renderscriptSupportModeEnabled true
                  }
              }
              
              android {
                  compileSdkVersion(33)
      
                  defaultConfig {
                      minSdkVersion(9)
                      targetSdkVersion(19)
      
                      renderscriptTargetApi = 18
                      renderscriptSupportModeEnabled = true
                  }
              }
              

      ऊपर दी गई सेटिंग, Android बिल्ड प्रोसेस में खास व्यवहार को कंट्रोल करती हैं:

      • renderscriptTargetApi - बाइट कोड वर्शन को तय करता है जनरेट किया गया. हमारा सुझाव है कि आप इस वैल्यू को एपीआई के सबसे निचले लेवल पर सेट करें, जो आपके इस्तेमाल किए जा रहे सभी फ़ंक्शन उपलब्ध करा सके. साथ ही, renderscriptSupportModeEnabled को true पर सेट करें. इस सेटिंग के लिए मान्य वैल्यू, कोई भी पूर्णांक वैल्यू होती है लेवल 11 से लेकर हाल ही में रिलीज़ किए गए एपीआई लेवल तक पहुंच सकता है. अगर आपके ऐप्लिकेशन मेनिफ़ेस्ट में, SDK टूल के कम से कम वर्शन के लिए बताई गई वैल्यू को किसी दूसरी वैल्यू पर सेट किया गया है, तो उस वैल्यू को अनदेखा कर दिया जाएगा. साथ ही, SDK टूल के कम से कम वर्शन को सेट करने के लिए, बिल्ड फ़ाइल में मौजूद टारगेट वैल्यू का इस्तेमाल किया जाएगा.
      • renderscriptSupportModeEnabled - इससे यह तय होता है कि जनरेट किए गए बाइटकोड को, काम करने वाले किसी ऐसे वर्शन पर स्विच कर दिया जाए जिस पर टारगेट वर्शन काम न करता हो.
  3. RenderScript का इस्तेमाल करने वाली अपनी ऐप्लिकेशन क्लास में, सहायता लाइब्रेरी की क्लास के लिए इंपोर्ट जोड़ें:
    KotlinJava
    import android.support.v8.renderscript.*
    import android.support.v8.renderscript.*;

Java या Kotlin कोड से RenderScript का इस्तेमाल करना

Java या Kotlin कोड से RenderScript का इस्तेमाल करने के लिए, android.renderscript या android.support.v8.renderscript पैकेज में मौजूद एपीआई क्लास का इस्तेमाल किया जाता है. ज़्यादातर ऐप्लिकेशन, इस्तेमाल के एक ही बुनियादी पैटर्न का पालन करते हैं:

  1. RenderScript कॉन्टेक्स्ट को शुरू करना. create(Context) की मदद से बनाया गया RenderScript कॉन्टेक्स्ट, यह पक्का करता है कि RenderScript का इस्तेमाल किया जा सकता है. साथ ही, यह बाद में इस्तेमाल होने वाले सभी RenderScript ऑब्जेक्ट के लाइफ़टाइम को कंट्रोल करने के लिए एक ऑब्जेक्ट उपलब्ध कराता है. आपको कॉन्टेंट के कॉन्टेक्स्ट के बारे में सोचना चाहिए बनाना लंबे समय तक चलने वाला ऑपरेशन बनाना होगा, क्योंकि इससे अलग-अलग हार्डवेयर के हिस्से; वह किसी ऐप्लिकेशन के क्रिटिकल पाथ में नहीं होना चाहिए, अगर किया जा सकता है. आम तौर पर, किसी ऐप्लिकेशन में एक बार में सिर्फ़ एक RenderScript कॉन्टेक्स्ट होता है.
  2. पास दिए जाने के लिए कम से कम एक Allocation बनाएं स्क्रिप्ट. Allocation एक ऐसा RenderScript ऑब्जेक्ट है जो तय सीमा तक डेटा सेव रखने में मदद करता है. स्क्रिप्ट में मौजूद कर्नेल, Allocation ऑब्जेक्ट को इनपुट और आउटपुट के तौर पर इस्तेमाल करते हैं. साथ ही, स्क्रिप्ट ग्लोबल के तौर पर बाउंड होने पर, Allocation ऑब्जेक्ट को rsGetElementAt_type() और rsSetElementAt_type() का इस्तेमाल करके कर्नेल में ऐक्सेस किया जा सकता है. Allocation ऑब्जेक्ट की मदद से, ऐरे को Java कोड से RenderScript कोड में और इसके उलट, RenderScript कोड से Java कोड में पास किया जा सकता है. Allocation ऑब्जेक्ट आम तौर पर इनका इस्तेमाल करके बनाए जाते हैं createTyped() या createFromBitmap().
  3. ज़रूरी स्क्रिप्ट बनाएं. RenderScript का इस्तेमाल करते समय, आपके पास दो तरह की स्क्रिप्ट उपलब्ध होती हैं:
    • ScriptC: ये उपयोगकर्ता की ओर से तय की गई स्क्रिप्ट हैं, जैसा कि ऊपर RenderScript कर्नेल लिखना में बताया गया है. हर स्क्रिप्ट में एक Java क्लास होती है, जिसे RenderScript कंपाइलर दिखाता है. इससे, Java कोड से स्क्रिप्ट को आसानी से ऐक्सेस किया जा सकता है. इस क्लास का नाम ScriptC_filename होता है. उदाहरण के लिए, अगर मैपिंग कर्नेल invert.rs में मौजूद हैं और RenderScript कॉन्टेक्स्ट पहले से mRenderScript, स्क्रिप्ट को इंस्टैंशिएट करने के लिए Java या Kotlin कोड इस तरह का होगा:
      KotlinJava
      val invert = ScriptC_invert(renderScript)
      ScriptC_invert invert = new ScriptC_invert(renderScript);
    • ScriptIntrinsic: ये सामान्य कार्रवाइयों के लिए, पहले से मौजूद RenderScript के कोर हैं. जैसे, गॉसियन ब्लर, कन्वोल्यूशन, और इमेज ब्लेंडिंग. ज़्यादा जानकारी के लिए, इसकी सब-क्लास देखें ScriptIntrinsic.
  4. ऐलोकेशन में डेटा का इस्तेमाल करें. createFromBitmap() की मदद से बनाए गए बजट को छोड़कर, किसी ऐलोकेशन में खाली डेटा अपने-आप भर जाता है. ऐसा तब होता है, जब वह ऐसा होता है सबसे पहले बनाया गया. किसी ऐलोकेशन को पॉप्युलेट करने के लिए, Allocation में "कॉपी" करने के किसी एक तरीके का इस्तेमाल करें. "कॉपी" करने के तरीके सिंक्रोनस होते हैं.
  5. ज़रूरी स्क्रिप्ट ग्लोबल सेट करें. आप set_globalname नाम वाली ScriptC_filename क्लास. इसके लिए उदाहरण के लिए, threshold नाम का int वैरिएबल सेट करने के लिए, Java का तरीका set_threshold(int); और सेट करने के लिए lookup नाम वाला एक rs_allocation वैरिएबल, Java का इस्तेमाल करें set_lookup(Allocation) तरीका. set तरीके एसिंक्रोनस होते हैं.
  6. ज़रूरी कर्नेल और कॉल किए जा सकने वाले फ़ंक्शन लॉन्च करें.

    किसी दिए गए कर्नेल को लॉन्च करने के तरीके, ScriptC_filename क्लास में दिखते हैं. इन तरीकों के नाम forEach_mappingKernelName() या reduce_reductionKernelName() होते हैं. ये लॉन्च एसिंक्रोनस होते हैं. कर्नेल के आर्ग्युमेंट के आधार पर, तरीका एक या ज़्यादा आवंटन लेता है और सभी में एक जैसे डाइमेंशन होने चाहिए. डिफ़ॉल्ट रूप से, कोई भी kernel उन डाइमेंशन के हर निर्देशांक पर लागू होता है. उन निर्देशांक के सबसेट पर कोई kernel लागू करने के लिए, forEach या reduce तरीके के आखिरी आर्ग्युमेंट के तौर पर कोई सही Script.LaunchOptions पास करें.

    उसी ScriptC_filename क्लास में मौजूद invoke_functionName तरीकों का इस्तेमाल करके, कॉल किए जा सकने वाले फ़ंक्शन लॉन्च करें. ये लॉन्च एसिंक्रोनस होते हैं.

  7. Allocation ऑब्जेक्ट से डेटा वापस पाएं और javaFutureType ऑब्जेक्ट. कार्रवाई करने के लिए किसी Allocation से Java कोड से डेटा ऐक्सेस करते हैं, तो आपको वह डेटा कॉपी करना होगा में से किसी एक "कॉपी" का इस्तेमाल करके Java पर वापस जाएं Allocation में बताया गया तरीका है. रिडक्शन कर्नेल का नतीजा पाने के लिए, आपको javaFutureType.get() तरीका इस्तेमाल करना होगा. "कॉपी" और get() तरीके सिंक्रोनस होते हैं.
  8. RenderScript के कॉन्टेक्स्ट को मिटाएं. destroy() का इस्तेमाल करके या RenderScript कॉन्टेक्स्ट ऑब्जेक्ट को ग़ैर-ज़रूरी डेटा के तौर पर हटाकर, RenderScript कॉन्टेक्स्ट को मिटाया जा सकता है. इससे उस कॉन्टेक्स्ट से जुड़े किसी भी ऑब्जेक्ट का इस्तेमाल करने पर, अपवाद दिखता है.

एसिंक्रोनस एक्ज़ीक्यूशन मॉडल

यह जानकारी forEach, invoke, reduce, और और set विधियां एसिंक्रोनस हैं -- प्रत्येक कार्रवाई का अनुरोध किया गया. हालांकि, अलग-अलग कार्रवाइयों को उसी क्रम में क्रम से लगाया जाता है जिस क्रम में उन्हें लॉन्च किया जाता है.

Allocation क्लास में "कॉपी" सुविधा का इस्तेमाल किया जाता है डेटा को कॉपी करने के तरीके और ऐलोकेशन से. "कॉपी" विधि सिंक्रोनस है और उसे किसी भी हैं जो उसी आवंटन को छूते हैं.

रिफ़्लेक्ट की गई javaFutureType क्लास, रिडक्शन का नतीजा पाने के लिए get() तरीका उपलब्ध कराती हैं. get() है सिंक्रोनस होता है और उसे रिडक्शन (जो एसिंक्रोनस है) के हिसाब से क्रम में लगाया जाता है.

सिंगल-सोर्स RenderScript

Android 7.0 (एपीआई लेवल 24) में पेश है सिंगल-सोर्स नाम की नई प्रोग्रामिंग सुविधा RenderScript, जिसमें कर्नेल उन स्क्रिप्ट से लॉन्च किए जाते हैं जहां उन्हें तय किया जाता है. ऐसा न करने पर किया जाता है इंटरनेट से मिलता है. फ़िलहाल, यह तरीका सिर्फ़ मैपिंग कर्नेल के लिए उपलब्ध है. कम शब्दों में बताने के लिए, इस सेक्शन में इन्हें सिर्फ़ "कर्नेल" कहा गया है. इस नई सुविधा की मदद से, स्क्रिप्ट में भी टाइप rs_allocation के ऐलोकेशन बनाए जा सकते हैं. अब यह काम किया जा सकता है पूरे एल्गोरिदम को एक स्क्रिप्ट में लागू करें, भले ही एक से ज़्यादा कर्नेल लॉन्च की ज़रूरत क्यों न हो. दो फ़ायदे हैं: ज़्यादा पढ़ा जा सकने वाला कोड, क्योंकि इससे एल्गोरिदम के लागू होने को एक भाषा; और संभावित रूप से तेज़ कोड, क्योंकि Java और के बीच कम ट्रांज़िशन की वजह से कई कर्नेल लॉन्च में RenderScript.

सिंगल-सोर्स RenderScript में, RenderScript के लिए कोर लिखना में बताए गए तरीके से कोर लिखे जाते हैं. इसके बाद, आप एक ऐसा फ़ंक्शन लिखते हैं जिसे रोका नहीं जा सकता. लॉन्च करने के लिए, rsForEach(). इस एपीआई का इस्तेमाल करने के लिए, पहले चरण के तौर पर कर्नेल फ़ंक्शन का इस्तेमाल किया जाता है पैरामीटर और उसके बाद इनपुट और आउटपुट आवंटन. मिलता-जुलता एक एपीआई rsForEachWithOptions(), rs_script_call_t टाइप का एक अतिरिक्त आर्ग्युमेंट लेता है. यह आर्ग्युमेंट, कोर फ़ंक्शन के लिए प्रोसेस करने के लिए, इनपुट और आउटपुट के एलोकेशन से एलिमेंट का सबसेट तय करता है.

RenderScript कैलकुलेशन शुरू करने के लिए, Java से invokable फ़ंक्शन को कॉल किया जाता है. Java कोड से RenderScript का इस्तेमाल करना में दिया गया तरीका अपनाएं. सही कर्नेल लॉन्च करें चरण में, कॉल करें invoke_function_name() का इस्तेमाल करके शुरू किया जा सकने वाला फ़ंक्शन, जो पूरे कंप्यूटेशन का इस्तेमाल करें. इसमें कर्नेल लॉन्च करना भी शामिल है.

सेव करने और पास करने के लिए अक्सर आवंटन की ज़रूरत होती है एक कर्नेल के लॉन्च से दूसरे में बीच के नतीजे. इन्हें बनाने के लिए, rsCreateAllocation() का इस्तेमाल करें. इस एपीआई का इस्तेमाल करना आसान है. इसमें rsCreateAllocation_<T><W>(…), एलिमेंट के लिए डेटा टाइप है और W, एलिमेंट के लिए वेक्टर की चौड़ाई है. एपीआई, Google Cloud में आर्ग्युमेंट के तौर पर डाइमेंशन X, Y, और Z. 1D या 2D ऐलोकेशन के लिए, डाइमेंशन Y या Z का साइज़ छोड़ा जा सकता है. उदाहरण के लिए, rsCreateAllocation_uchar4(16384), 16384 एलिमेंट का एक 1D ऐलोकेशन बनाता है. इनमें से हर एलिमेंट uchar4 टाइप का होता है.

बजट का बंटवारा, सिस्टम अपने-आप करता है. आपको उन्हें साफ़ तौर पर रिलीज़ करने या मुक्त करने की ज़रूरत नहीं है. हालांकि, आपके पास कॉल करने का विकल्प है rsClearObject(rs_allocation* alloc) का इस्तेमाल करके बताएं कि अब आपको हैंडल की ज़रूरत नहीं है मौजूदा ऐलोकेशन के लिए alloc, ताकि सिस्टम को जल्द से जल्द संसाधनों का ऐक्सेस मिल सके.

RenderScript कर्नेल लिखना सेक्शन में एक उदाहरण है कर्नल है, जो किसी इमेज को इनवर्ट करता है. यहां दिए गए उदाहरण में, एक इमेज पर एक से ज़्यादा इफ़ेक्ट लागू करने के बारे में बताया गया है. इसके लिए, सिंगल-सोर्स RenderScript का इस्तेमाल किया गया है. इसमें एक और कर्नेल, greyscale शामिल है, जो रंगीन इमेज को ब्लैक ऐंड व्हाइट में बदल देता है. इसके बाद, इनवोक किया जा सकने वाला फ़ंक्शन process(), उन दो कर्नेल को किसी इनपुट इमेज पर लगातार लागू करता है और एक आउटपुट इमेज बनाता है. इनपुट और आउटपुट को टाइप के आर्ग्युमेंट के तौर पर पास किया जाता है 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);
}

Java या Kotlin से process() फ़ंक्शन को इस तरह कॉल किया जा सकता है:

KotlinJava
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)
// 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);

इस उदाहरण में दिखाया गया है कि दो कर्नेल लॉन्च वाले एल्गोरिदम को, RenderScript भाषा में पूरी तरह से कैसे लागू किया जा सकता है. सिंगल-सोर्स के बिना RenderScript के लिए, आपको कर्नेल लॉन्च को अलग करते हुए Java कोड से दोनों कर्नेल लॉन्च करने होंगे पूरे एल्गोरिदम को समझना मुश्किल होता है. न सिर्फ़ सिंगल-सोर्स रेंडर स्क्रिप्ट कोड को पढ़ना आसान होता है, लेकिन यह ट्रांज़िशन को भी खत्म कर देता है कर्नेल लॉन्च में Java और स्क्रिप्ट के बीच फ़र्क़ करें. कुछ बार-बार होने वाले एल्गोरिदम, कर्नेल लॉन्च कर सकते हैं जिससे इस तरह का बदलाव काफ़ी अहम हो जाता है.

स्क्रिप्ट ग्लोबल

स्क्रिप्ट ग्लोबल, एक ऐसी साधारण भाषा है जो static की नहीं है स्क्रिप्ट (.rs) फ़ाइल में ग्लोबल वैरिएबल. filename.rs फ़ाइल में तय की गई var नाम की स्क्रिप्ट ग्लोबल के लिए, ScriptC_filename क्लास में एक तरीका get_var दिखेगा. दुनिया भर में const है, ऐसी स्थिति में भी set_var तरीका.

दी गई स्क्रिप्ट ग्लोबल में दो अलग-अलग वैल्यू होती हैं -- Java और एक script मान. इन वैल्यू का इस्तेमाल इस तरह किया जाता है:

  • अगर var के पास स्क्रिप्ट में स्टैटिक शुरू करने वाला टूल है, तो Java और दोनों में var का शुरुआती मान तय करता है स्क्रिप्ट. नहीं तो, वह शुरुआती वैल्यू शून्य होती है.
  • स्क्रिप्ट के अंदर var का ऐक्सेस, उसे पढ़ और लिखता है स्क्रिप्ट मान.
  • get_var तरीका, Java को पढ़ता है वैल्यू.
  • set_var तरीका (अगर यह मौजूद है) यह बताता है कि Java वैल्यू तुरंत सेट करता है और स्क्रिप्ट वैल्यू को लिखता है एसिंक्रोनस तरीके से.

ध्यान दें: इसका मतलब है कि स्क्रिप्ट में मौजूद किसी भी स्टैटिक शुरू करने वाले के अलावा, स्क्रिप्ट में ग्लोबल से लिखी गई वैल्यू, Java को नहीं दिखती हैं.

डेप्थ में रिडक्शन कर्नेल

कम करना, डेटा के कलेक्शन को एक साथ जोड़ने की प्रोसेस है वैल्यू. यह समानांतर प्रोग्रामिंग में एक उपयोगी सिद्धांत है, जिसमें फ़ॉलो किया जा रहा है:

  • सारे डेटा पर योग या गुणनफल की गणना करना
  • कंप्यूटिंग लॉजिकल ऑपरेशन (and, or, xor) पूरे डेटा पर
  • आंकड़ों में सबसे छोटी या सबसे बड़ी वैल्यू पता करना
  • डेटा में किसी खास वैल्यू या किसी खास वैल्यू के कोऑर्डिनेट को खोजना

Android 7.0 (एपीआई लेवल 24) और उसके बाद के वर्शन में, RenderScript कम करने वाले कर्नेल के साथ काम करता है. इससे, के सुझाव देते हैं. इनपुट पर रिडक्शन कर्नेल लॉन्च करने के लिए, 1, 2 या 3 डाइमेंशन.

ऊपर दिए गए उदाहरण में, एक सामान्य addint रिडक्शन कर्नेल दिखाया गया है. यहां findMinAndMax का ज़्यादा जटिल रिडक्शन कर्नेल दिया गया है जो टेबल में सबसे कम और ज़्यादा से ज़्यादा long वैल्यू की जगह 1-डाइमेंशन Allocation:

#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 रनटाइम एक या उससे ज़्यादा वैरिएबल बनाता है. इन्हें ऐक्यूमुलेटर डेटा आइटम कहा जाता है. इनका इस्तेमाल, रिडक्शन प्रोसेस की स्थिति को सेव करने के लिए किया जाता है. RenderScript रनटाइम परफ़ॉर्मेंस को बेहतर बनाने के लिए, इस तरह से क्वेरी के डेटा आइटम की संख्या चुनता है. Accumulator डेटा आइटम (accumType) का टाइप, kernel के accumulator फ़ंक्शन से तय होता है -- उस फ़ंक्शन का पहला आर्ग्युमेंट, Accumulator डेटा आइटम का पॉइंटर होता है. डिफ़ॉल्ट रूप से, हर कंपनी डेटा आइटम की शुरुआत शून्य से शुरू होती है (जैसे कि memset तक; हालांकि, कुछ करने के लिए, इनीशियलाइज़र फ़ंक्शन लिखा जा सकता है अलग हैं.

उदाहरण: addint कर्नल में, इनपुट वैल्यू जोड़ने के लिए int टाइप के इकट्ठा करने वाले डेटा आइटम का इस्तेमाल किया जाता है. कोई इनीशियलाइज़र फ़ंक्शन नहीं होता है, इसलिए हर अक्युमिलेटर डेटा आइटम को शुरू किया जाता है शून्य.

उदाहरण: इसमें findMinAndMax कर्नेल, एक्यूमिलेटर डेटा आइटम (MinAndMax प्रकार के) का इस्तेमाल कम से कम और ज़्यादा से ज़्यादा वैल्यू को ट्रैक करने के लिए किया जाता है मिला है. इन वैल्यू को क्रमशः LONG_MAX और LONG_MIN पर सेट करने के लिए, एक इनिशलाइज़र फ़ंक्शन होता है. साथ ही, इन वैल्यू की जगहों को -1 पर सेट करने के लिए, यह दिखाया जाता है कि प्रोसेस किए गए इनपुट के (खाली) हिस्से में वैल्यू मौजूद नहीं हैं.

Renderस्क्रिप्ट आपके अक्युमिलेटर फ़ंक्शन को एक बार, इनपुट. आम तौर पर, आपके फ़ंक्शन को किसी न किसी तरह से अक्युमिलेटर डेटा आइटम अपडेट करना चाहिए .

उदाहरण: addint कर्नल में, Accumulator फ़ंक्शन, Accumulator डेटा आइटम में किसी इनपुट एलिमेंट की वैल्यू जोड़ता है.

उदाहरण: findMinAndMax केर्नेल में, Accumulator फ़ंक्शन यह जांच करता है कि किसी इनपुट एलिमेंट की वैल्यू, Accumulator डेटा आइटम में रिकॉर्ड की गई सबसे कम वैल्यू से कम या उसके बराबर है या नहीं और/या Accumulator डेटा आइटम में रिकॉर्ड की गई सबसे ज़्यादा वैल्यू से ज़्यादा या उसके बराबर है या नहीं. इसके हिसाब से, Accumulator डेटा आइटम को अपडेट किया जाता है.

इनपुट में हर कोऑर्डिनेट के लिए, एक बार अक्यूम्युलेटर फ़ंक्शन को कॉल करने के बाद, RenderScript को को अक्युलेटर को जोड़ना चाहिए डेटा आइटम को एक साथ एक संचय डेटा आइटम में बदल देता है. आप चाहें तो एक कॉम्बिनर, लिख सकते हैं फ़ंक्शन का इस्तेमाल करें. अगर Accumulator फ़ंक्शन में एक इनपुट है और कोई खास आर्ग्युमेंट नहीं है, तो आपको Combiner फ़ंक्शन लिखने की ज़रूरत नहीं है. RenderScript, Accumulator डेटा आइटम को जोड़ने के लिए Accumulator फ़ंक्शन का इस्तेमाल करेगा. (अगर आपको यह डिफ़ॉल्ट तरीका पसंद नहीं है, तो अब भी आपके पास कॉम्बाइनर फ़ंक्शन लिखने का विकल्प है.)

उदाहरण: addint कर्नल में, कोई कंबाइनर फ़ंक्शन नहीं है. इसलिए, इसके लिए इकट्ठा करने वाले फ़ंक्शन का इस्तेमाल किया जाएगा. यह सही तरीका है, क्योंकि अगर हम वैल्यू के कलेक्शन को दो हिस्सों में बांटते हैं और उन दो हिस्सों की वैल्यू को अलग-अलग जोड़ते हैं, तो उन दो योगों को जोड़ने का मतलब पूरे कलेक्शन को जोड़ने से है.

उदाहरण: इसमें findMinAndMax कर्नेल, कंबाइनर फ़ंक्शन यह देखने के लिए जांच करता है कि "स्रोत" में रिकॉर्ड किया गया कम से कम मान अक्यूम्युलेटर डेटा आइटम *val, "डेस्टिनेशन" में रिकॉर्ड किए गए कम से कम मान से कम है अक्यूम्युलेटर डेटा आइटम *accum और अपडेट *accum भुगतान करते हैं. यह सबसे बड़ी वैल्यू के लिए भी इसी तरह काम करता है. इससे *accum को उस स्थिति में अपडेट कर दिया जाता है जो तब होती, जब सभी इनपुट वैल्यू को *accum में इकट्ठा किया जाता, न कि कुछ को *accum और कुछ को *val में.

सभी इकट्ठा किए गए डेटा आइटम को जोड़ने के बाद, RenderScript यह तय करता है कि Java में वापस जाने के लिए, डेटा को कितना कम किया जाए. ऐसा करने के लिए, outconverter फ़ंक्शन लिखा जा सकता है. अगर आप चाहें, तो आपको आउटकन्वर्टर फ़ंक्शन लिखने की ज़रूरत नहीं है रिडक्शन के नतीजे के तौर पर मिले-जुले अक्युमिलेटर डेटा आइटम की फ़ाइनल वैल्यू.

उदाहरण: addint कर्नेल में, outconverter फ़ंक्शन नहीं है. संयुक्त डेटा आइटम का अंतिम मान कुल योग होता है इनपुट के सभी एलिमेंट, जो वह मान है जिसे हम लौटाना चाहते हैं.

उदाहरण: इसमें findMinAndMax कर्नेल, आउट-कन्वर्टर फ़ंक्शन कम से कम स्थानों को होल्ड करने के लिए int2 परिणाम मान को शुरू करता है और यह ज़्यादा से ज़्यादा वैल्यू को क्रेडिट देने वाले सभी डेटा आइटम के कॉम्बिनेशन से मिलता है.

रिडक्शन कर्नेल लिखना

#pragma rs reduce, रिडक्शन कर्नेल को इसके हिसाब से तय करता है इसके नाम के साथ-साथ, उन फ़ंक्शन के नाम और भूमिकाएं भी तय करते हुए कर्नेल को ऊपर ले जाएं. ऐसे सभी फ़ंक्शन static. रिडक्शन कर्नेल के लिए, हमेशा accumulator की ज़रूरत होती है फ़ंक्शन; इस बात पर निर्भर करते हुए कि कुछ या सभी फ़ंक्शन को छोड़ा जा सकता है, करने के लिए कर्नेल.

#pragma rs reduce(kernelName) \
  initializer(initializerName) \
  accumulator(accumulatorName) \
  combiner(combinerName) \
  outconverter(outconverterName)

#pragma में दिए गए आइटम का मतलब यह है:

  • reduce(kernelName) (ज़रूरी): इससे पता चलता है कि रिडक्शन कर्नेल परिभाषित किया जा रहा है. लागू किए गए Java तरीके reduce_kernelName से, कर्नेल.
  • initializer(initializerName) (ज़रूरी नहीं): यह बताता है कि इस रिडक्शन कर्नेल के लिए, इनीशियलाइज़र फ़ंक्शन का इस्तेमाल करें. जब आप कर्नेल को लॉन्च करते हैं, तो RenderScript कॉल इस फ़ंक्शन को हर अक्यूम्युलेटर डेटा आइटम के लिए एक बार सबमिट किया जाता है. फ़ंक्शन को इस तरह से तय किया जाना चाहिए:

    static void initializerName(accumType *accum) {  }

    accum, इस फ़ंक्शन को शुरू करने के लिए, इकट्ठा किए गए डेटा आइटम का पॉइंटर है.

    अगर कोई इनिशलाइज़र फ़ंक्शन नहीं दिया जाता है, तो RenderScript हर इकट्ठा किए गए डेटा आइटम को शून्य पर शुरू करता है (जैसे कि memset के ज़रिए), जैसे कि कोई इनिशलाइज़र फ़ंक्शन मौजूद हो, जो इस तरह दिखता है:

    static void initializerName(accumType *accum) {
      memset(accum, 0, sizeof(*accum));
    }
  • accumulator(accumulatorName) (ज़रूरी है): इस रिडक्शन कर्नेल के लिए, इकट्ठा करने वाले फ़ंक्शन का नाम बताता है. जब Kernel को लॉन्च किया जाता है, तो RenderScript, इनपुट में मौजूद हर निर्देशांक के लिए, इस फ़ंक्शन को एक बार कॉल करता है. ऐसा, इनपुट के हिसाब से किसी तरह से Accumulator डेटा आइटम को अपडेट करने के लिए किया जाता है. फ़ंक्शन इस तरह परिभाषित होना चाहिए:

    static void accumulatorName(accumType *accum,
                                in1Type in1, , inNType inN
                                [, specialArguments]) {}

    accum इस फ़ंक्शन के लिए, अक्युमिलेटर डेटा आइटम का पॉइंटर है बदलें. in1 से inN तक एक या उससे ज़्यादा आर्ग्युमेंट होते हैं. ये आर्ग्युमेंट, कर्नेल लॉन्च के लिए दिए गए इनपुट के आधार पर अपने-आप भर जाते हैं. हर इनपुट के लिए एक आर्ग्युमेंट. अक्यूम्युलेटर फ़ंक्शन इनमें से कोई भी खास आर्ग्युमेंट ले सकता है.

    एक से ज़्यादा इनपुट वाले केर्नल का उदाहरण dotProduct है.

  • combiner(combinerName)

    (ज़रूरी नहीं): इसके लिए कंबाइनर फ़ंक्शन का नाम बताता है रिडक्शन कर्नेल. रेंडर करने से पहले, अक्यूम्युलेटर फ़ंक्शन को कॉल करने के बाद इनपुट में हर निर्देशांक के लिए एक बार, यह फ़ंक्शन को कई सभी मेमोरी आइटम को एक साथ जोड़ने के लिए, ज़रूरत के हिसाब से समय कैश मेमोरी में सेव किया गया डेटा आइटम. फ़ंक्शन इस तरह से तय होना चाहिए:

    static void combinerName(accumType *accum, const accumType *other) {  }

    accum, "डेस्टिनेशन" का पॉइंटर है इसके लिए अक्युमिलेटर डेटा आइटम फ़ंक्शन का इस्तेमाल करें. other, *accum में "जोड़ने" के लिए, इस फ़ंक्शन के "सोर्स" इकट्ठा करने वाले डेटा आइटम का पॉइंटर है.

    ध्यान दें: ऐसा किया जा सकता है *accum, *other या दोनों को शुरू किया जा चुका है, लेकिन कभी शुरू नहीं किया गया कोअक्यूम्युलेटर फ़ंक्शन में पास किया गया है; इसका मतलब है कि एक या दोनों को कभी अपडेट नहीं किया गया है के हिसाब से फ़िल्टर करें. उदाहरण के लिए, findMinAndMax केर्नेल में, fMMCombiner फ़ंक्शन, idx < 0 की साफ़ तौर पर जांच करता है, क्योंकि यह ऐसे इकट्ठा किए गए डेटा आइटम की जानकारी देता है जिसकी वैल्यू INITVAL होती है.

    अगर कंबाइनर फ़ंक्शन उपलब्ध नहीं कराया जाता है, तो RenderScript इस जगह सेट करते समय, यह देखते हुए कि कंबाइनर फ़ंक्शन मौजूद है जो इस तरह दिखता है:

    static void combinerName(accumType *accum, const accumType *other) {
      accumulatorName(accum, *other);
    }

    अगर कोर में एक से ज़्यादा इनपुट हैं, इनपुट डेटा का टाइप और इकट्ठा करने वाले फ़ंक्शन का डेटा टाइप एक जैसा नहीं है या इकट्ठा करने वाले फ़ंक्शन में एक या उससे ज़्यादा खास आर्ग्युमेंट हैं, तो कॉम्बाइनर फ़ंक्शन ज़रूरी है.

  • outconverter(outconverterName) (ज़रूरी नहीं): इस रिडक्शन कर्नेल के लिए, आउटकन्वर्टर फ़ंक्शन का नाम बताता है. RenderScript, सभी इकट्ठा किए गए डेटा आइटम को जोड़ने के बाद, इस फ़ंक्शन को कॉल करता है. इससे, Java में वापस जाने के लिए डेटा कम करने का नतीजा तय होता है. फ़ंक्शन को इस तरह से तय करना चाहिए:

    static void outconverterName(resultType *result, const accumType *accum) {  }

    result, नतीजे के डेटा आइटम का पॉइंटर है. इसे रेंडरस्क्रिप्ट रनटाइम ने ऐलोकेट किया है, लेकिन इसे शुरू नहीं किया है. इस फ़ंक्शन को कम करने के नतीजे के साथ शुरू करने के लिए, result का इस्तेमाल किया जाता है. resultType, उस डेटा आइटम का टाइप है. यह accumType से मेल नहीं खाना चाहिए. accum, फ़ाइनल अक्युमिलेटर डेटा आइटम का पॉइंटर है इसकी गणना कॉम्बिनर फ़ंक्शन से की जाती है.

    अगर कोई आउटकन्वर्टर फ़ंक्शन नहीं दिया जाता है, तो RenderScript आखिरी इकट्ठा किए गए डेटा आइटम को नतीजे के डेटा आइटम में कॉपी कर देता है. ऐसा करने पर, यह ऐसा लगता है जैसे कोई आउटकन्वर्टर फ़ंक्शन है, जो इस तरह दिखता है:

    static void outconverterName(accumType *result, const accumType *accum) {
      *result = *accum;
    }

    अगर आपको इंक्यूलेटर के डेटा टाइप से अलग तरह का नतीजा चाहिए, तो आउटकन्वर्टर फ़ंक्शन ज़रूरी है.

ध्यान दें कि किसी कर्नेल में इनपुट टाइप, एक्यूमुलेटर डेटा आइटम टाइप, और नतीजा टाइप होते हैं. इनमें से किसी भी टाइप का एक जैसा होना ज़रूरी नहीं है. उदाहरण के लिए, findMinAndMax केर्नेल में, इनपुट टाइप long, इकट्ठा किए गए डेटा आइटम का टाइप MinAndMax, और नतीजे का टाइप int2 अलग-अलग होते हैं.

आपको क्या नहीं लगता?

किसी दिए गए कर्नेल लॉन्च के लिए, आपको RenderScript के बनाए गए इकट्ठा किए गए डेटा आइटम की संख्या पर भरोसा नहीं करना चाहिए. इस बात की कोई गारंटी नहीं है कि एक ही इनपुट के साथ एक ही केरल के दो लॉन्च से, एक ही संख्या में इकट्ठा किए गए डेटा आइटम बनेंगे.

आपको उस क्रम पर भरोसा नहीं करना चाहिए जिसमें RenderScript, शुरू करने वाले को कॉल करता है, और कंबाइनर फ़ंक्शन; साथ ही, इनमें से कुछ के नाम भी रखे जा सकते हैं. इस बात की कोई गारंटी नहीं है कि एक ही इनपुट वाले एक कर्नेल के दो लॉन्च एक ही क्रम में होंगे. सिर्फ़ गारंटी यह है कि सिर्फ़ इनिशलाइज़र फ़ंक्शन में, शुरू नहीं किया गया अक्युमिलेटर कभी भी दिखेगा डेटा आइटम. उदाहरण के लिए:

  • इस बात की कोई गारंटी नहीं है कि Accumulator फ़ंक्शन को कॉल करने से पहले, सभी Accumulator डेटा आइटम को शुरू किया जाएगा. हालांकि, इसे सिर्फ़ शुरू किए गए Accumulator डेटा आइटम पर कॉल किया जाएगा.
  • इस बात की कोई गारंटी नहीं है कि इनपुट एलिमेंट, Accumulator फ़ंक्शन में किस क्रम में पास किए जाएंगे.
  • इस बात की कोई गारंटी नहीं है कि combiner फ़ंक्शन को कॉल करने से पहले, सभी इनपुट एलिमेंट के लिए Accumulator फ़ंक्शन को कॉल किया गया हो.

इसका एक नतीजा यह है कि findMinAndMax कोर को नतीजा तय करने के लिए कोई फ़ॉर्मूला नहीं दिया जाता: अगर इनपुट में एक ही कम से कम या ज़्यादा से ज़्यादा वैल्यू एक से ज़्यादा बार दिखती है, तो यह पता नहीं चलता कि कोर किस वैल्यू को ढूंढेगा.

आपको किस चीज़ की गारंटी देनी चाहिए?

RenderScript सिस्टम, किसी भी कोर को कई अलग-अलग तरीकों से चला सकता है. इसलिए, आपको कुछ नियमों का पालन करना होगा, ताकि आपका कोर आपकी पसंद के मुताबिक काम करे. इन नियमों का पालन न करने पर, हो सकता है कि आपको गलत नतीजे दिखें, रनटाइम से जुड़ी गड़बड़ियों के बारे में बताना चाहिए.

नीचे दिए गए नियमों में अक्सर यह बताया जाता है कि दो इकट्ठा करने वाले डेटा आइटम में "एक ही वैल्यू" होनी चाहिए. इसका क्या अर्थ है? यह इस बात पर निर्भर करता है कि आप कर्नेल से क्या करवाना चाहते हैं. इसके लिए जैसे गणितीय छोटा करके addint लिखना, आम तौर पर सही होता है "एक जैसे" के लिए का मतलब गणितीय समानता से है. "कोई भी चुनें" खोज के लिए, findMinAndMax ("इनपुट वैल्यू की सबसे कम और सबसे ज़्यादा वैल्यू की जगह ढूंढें") जैसी खोज में, एक जैसी इनपुट वैल्यू एक से ज़्यादा बार हो सकती हैं. ऐसे में, किसी इनपुट वैल्यू की सभी जगहों को "एक जैसा" माना जाना चाहिए. आप लिख सकते हैं "सबसे बाईं और सबसे ज़्यादा इनपुट वैल्यू की जगह का पता लगाने" के लिए मिलता-जुलता कर्नेल जहां (जैसे) स्थान पर एक समान कम से कम मान के मुकाबले स्थान 100 पर एक कम से कम मान को प्राथमिकता दी जाती है 200; इस कर्नेल के लिए, "वही" का मतलब एक समान location होगा, न कि सिर्फ़ एक जैसी value है और अक्यूम्युलेटर और कंबाइनर फ़ंक्शन ऐसे होने चाहिए यह findMinAndMax से अलग है.

इनिटिलाइज़र फ़ंक्शन को आइडेंटिटी वैल्यू बनानी होगी. इसका मतलब है कि अगर I और A, अक्युमिलेटर डेटा आइटम शुरू किए गए हैं शुरू करने वाले फ़ंक्शन से होता है और I को कभी भी अक्यूमुलेटर फ़ंक्शन (लेकिन A हो सकता है), फिर
  • combinerName(&A, &I) को यह करना चाहिए A को अभी जैसा रहने दें
  • combinerName(&I, &A) के लिए ज़रूरी है कि वह I को A के जैसा ही रखे

उदाहरण: addint में कर्नेल के तौर पर, एक अक्युमिलेटर डेटा आइटम शून्य पर शुरू किया जाता है. इसके लिए कंबाइनर फ़ंक्शन कर्नेल जोड़ करता है; जोड़ने के लिए आइडेंटिटी वैल्यू, शून्य है.

उदाहरण: findMinAndMax के kernel में, INITVAL पर एक इकट्ठा करने वाले डेटा आइटम को शुरू किया जाता है.

  • fMMCombiner(&A, &I), A को छोड़ देता है, क्योंकि I INITVAL है.
  • fMMCombiner(&I, &A) सेट I A को, क्योंकि I INITVAL है.

इसलिए, INITVAL असल में पहचान की वैल्यू है.

कॉम्बाइनर फ़ंक्शन, कम्यूटेटिव होना चाहिए. इसका मतलब है कि अगर A और B, अक्युमिलेटर डेटा आइटम शुरू किए गए हैं शुरुआती फ़ंक्शन से पास किया जा सकता है और हो सकता है कि इसे अक्यूम्युलेटर फ़ंक्शन शून्य पर भेजा गया हो या ज़्यादा बार, तो combinerName(&A, &B) को A को उसी मान पर सेट करें वह combinerName(&B, &A) B को सेट करता है.

उदाहरण: addint में कर्नेल, कंबाइनर फ़ंक्शन, दो अक्युमिलेटर डेटा आइटम वैल्यू जोड़ता है; इसके अलावा कम्यूटेटिव.

उदाहरण: findMinAndMax कर्नेल में, fMMCombiner(&A, &B) और A = minmax(A, B) और minmax क्रमिक रूप से इस्तेमाल होते हैं, इसलिए fMMCombiner भी ऐसा ही है.

कॉम्बाइनर फ़ंक्शन, असोसिएटिव होना चाहिए. इसका मतलब है कि अगर A, B, और C, एक ही वैल्यू पर सेट किए गए ऐसे इकट्ठा करने वाले डेटा आइटम हैं जिन्हें initializer फ़ंक्शन से शुरू किया गया है और जिन्हें इकट्ठा करने वाले फ़ंक्शन में शून्य या उससे ज़्यादा बार पास किया गया है, तो नीचे दिए गए दो कोड क्रम में A को एक ही वैल्यू पर सेट करना होगा:

  • combinerName(&A, &B);
    combinerName(&A, &C);
  • combinerName(&B, &C);
    combinerName(&A, &B);

उदाहरण: addint कर्नेल में, कंबाइनर फ़ंक्शन, दो अक्युमिलेटर डेटा आइटम की वैल्यू जोड़ता है:

  • 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)
इसलिए, दोनों क्रम ये हैं
  • 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 भी है.

अक्यूम्युलेटर फ़ंक्शन और कंबाइनर फ़ंक्शन को एक साथ बेसिक नियमों का पालन करना चाहिए फ़ोल्ड करने का नियम. इसका मतलब है कि अगर A और B, इनऐक्टिव डेटा आइटम हैं, A इनिशलाइज़र फ़ंक्शन से शुरू किया गया और हो सकता है कि इसे अक्यूम्युलेटर फ़ंक्शन में पास किया गया हो शून्य या उससे ज़्यादा बार, B को शुरू नहीं किया गया है और आर्ग अक्यूम्युलेटर को किए गए किसी खास कॉल के लिए, इनपुट आर्ग्युमेंट और खास आर्ग्युमेंट की सूची फ़ंक्शन है, तो नीचे दिए गए दो कोड क्रमों को 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 एक जैसे हैं
  • तीसरा स्टेटमेंट, B += V जैसा ही है, जो B = V जैसा ही है
  • चौथा स्टेटमेंट, A += B जैसा ही है, जो A += V जैसा ही है

स्टेटमेंट 1 और 4, A को एक ही वैल्यू पर सेट करते हैं. इसलिए, यह केरल फ़ोल्ड करने के बुनियादी नियम का पालन करता है.

उदाहरण: किसी इनपुट के लिए, findMinAndMax कर्नेल में X कोऑर्डिनेट पर वैल्यू V:

  • स्टेटमेंट 1, A = minmax(A, IndexedVal(V, X)) जैसा ही है
  • दूसरा स्टेटमेंट, 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 है, तो दस्तावेज़ में तीन विधियां क्लास ScriptC_filename:

KotlinJava
// 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
// 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 कर्नेल को कॉल करने के कुछ उदाहरण दिए गए हैं:

KotlinJava
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()
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();

पहले तरीके में, कोर के accumulator फ़ंक्शन में हर इनपुट आर्ग्युमेंट के लिए एक इनपुट Allocation आर्ग्युमेंट होता है. RenderScript रनटाइम जांच करके यह पक्का करता है कि सभी इनपुट ऐलोकेशन के डाइमेंशन एक जैसे हैं. साथ ही, इनमें से हर एक का Element टाइप इनपुट ऐलोकेशन, अक्यूम्युलेटर के इनपुट आर्ग्युमेंट से मेल खाता है फ़ंक्शन का प्रोटोटाइप. अगर इनमें से कोई भी जांच पूरी नहीं हो पाती है, तो रेंडर स्क्रिप्ट में एक अपवाद होता है. kernel उन डाइमेंशन में हर कोऑर्डिनेट पर लागू होता है.

दूसरा तरीका और पहला तरीका, दोनों एक ही हैं. हालांकि, दूसरे तरीके और तर्क sc, जिसका इस्तेमाल कर्नेल निष्पादन को निर्देशांक.

तीसरा तरीका और पहला तरीका, दोनों एक ही हैं. हालांकि, ऐलोकेशन इनपुट के बजाय, यह Java अरे इनपुट की मदद लेता है. यह एक सुविधा है, इससे आपको, साफ़ तौर पर आवंटन बनाने और डेटा कॉपी करने के लिए, कोड लिखने की ज़रूरत नहीं पड़ती किसी Java कलेक्शन से निकाला जा सकता है. हालांकि, पहले तरीके के बजाय तीसरे तरीके का इस्तेमाल करने से, कोड की परफ़ॉर्मेंस बेहतर नहीं होती. हर इनपुट अरे के लिए, तीसरा तरीका कुछ समय के लिए सही Element टाइप के साथ 1-डाइमेंशन वाला ऐलोकेशन और setAutoPadding(boolean) चालू हो जाता है और अरे को असाइन करें, जैसे कि Allocation के copyFrom() तरीके से. इसके बाद, यह उन अस्थायी आवंटनों को पास करके, पहला तरीका इस्तेमाल करता है.

ध्यान दें: अगर आपका ऐप्लिकेशन एक ही ऐरे या एक ही डाइमेंशन और एलिमेंट टाइप के अलग-अलग ऐरे के साथ कई कर्नेल कॉल करेगा, तो तीसरे तरीके का इस्तेमाल करने के बजाय, खुद एलोकेशन बनाकर, उन्हें पॉप्युलेट करके, और फिर से इस्तेमाल करके परफ़ॉर्मेंस को बेहतर बनाया जा सकता है.

javaFutureType, reflected reduction methods का रिटर्न टाइप है. यह ScriptC_filename क्लास में, रिफ़्लेक्ट की गई स्टैटिक नेस्ट की गई क्लास है. यह कम किए गए कोर के साथ, कर्नेल के चलने के नतीजे के बारे में बताता है. रन का असल नतीजा पाने के लिए, उस क्लास के get() तरीके को कॉल करें, जो javaResultType टाइप की वैल्यू दिखाता है. get() सिंक्रोनस है.

KotlinJava
class ScriptC_filename(rs: RenderScript) : ScriptC(…) {
    object javaFutureType {
        fun get(): javaResultType {}
    }
}
public class ScriptC_filename extends ScriptC {
  public static class javaFutureType {
    public javaResultType get() {}
  }
}

java resultType और उसके resultType से तय आउट कन्वर्टर फ़ंक्शन. जब तक कि resultType एक साइन नहीं किया गया टाइप (स्केलर, वेक्टर या अरे), javaनतीजेType सीधे तौर पर संबंधित हैं 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], फिर javaresultsType long, Long2 या long[] है. resultType की कुछ वैल्यू ऐसी होती हैं जिन्हें javaResultType से दिखाया नहीं जा सकता.

javaFutureType, आने वाले समय में मिलने वाले नतीजे का टाइप है. यह outconverter फ़ंक्शन के resultType से जुड़ा होता है.

  • अगर resultType कोई सरणी प्रकार नहीं है, तो javaFutureType result_resultType है.
  • अगर resultType प्रकार memberType के सदस्यों के साथ लंबाई Count की श्रेणी है, तो javaFutureType resultArrayCount_memberType है.

उदाहरण के लिए:

KotlinJava
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> =     }
}
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() {}
  }
}

अगर javaresultsType एक ऑब्जेक्ट टाइप है (इसमें अरे टाइप भी शामिल है), तो हर कॉल चुने गए इंस्टेंस पर javaFutureType.get() को सेट करने पर, नतीजे के तौर पर वही दिखेगा ऑब्जेक्ट है.

अगर javaResultType, resultType टाइप की सभी वैल्यू को दिखा नहीं सकता और कोई कम करने वाला केरल, ऐसी वैल्यू दिखाता है जिसे दिखाया नहीं जा सकता, तो javaFutureType.get() एक अपवाद दिखाता है.

तीसरा तरीका और devecSiInXType

devecSiInXType, accumulator फ़ंक्शन के संबंधित आर्ग्युमेंट के inXType से जुड़ा Java टाइप है. अगर inXType, बिना साइन वाला टाइप या वैक्टर टाइप नहीं है, तो devecSiInXType, सीधे तौर पर उससे जुड़ा Java टाइप है. अगर inXType, बिना साइन वाला स्केलर टाइप है, तो devecSiInXType, एक ही साइज़ के साइन वाले स्केलर टाइप से सीधे तौर पर जुड़ा हुआ Java टाइप है. अगर inXType, हस्ताक्षर वाला वेक्टर टाइप है, तो devecSiInXType, वेक्टर कॉम्पोनेंट टाइप से सीधे तौर पर जुड़ा Java टाइप है. अगर inXType को साइन नहीं किया गया है, तो वेक्टर टाइप, तो devecSiInXType वह Java टाइप है जो सीधे तौर पर साइन किए गए स्केलर टाइप का साइज़, वेक्टर कॉम्पोनेंट टाइप के साइज़ के बराबर होता है. उदाहरण के लिए:

  • अगर inXType int है, तो devecSiInXType int है.
  • अगर inXType int2 है, तो devecSiInXType int है. यह अरे फ़्लैट किया गया रिप्रज़ेंटेशन है: इसमें आवंटन के रूप में, कई स्केलर एलिमेंट में दो कॉम्पोनेंट वेक्टर होते हैं एलिमेंट. यह वही तरीका है जिससे Allocation के copyFrom() तरीके काम करते हैं.
  • अगर inXType को uint है, तो deviceSiInXType int है. Java श्रेणी में, हस्ताक्षर किए गए मान को आवंटन में एक जैसा बिटपैटर्न. यह copyFrom() की तरह ही होता है Allocation काम करता है.
  • अगर inXType uint2 है, तो deviceSiInXType int है. यह int2 और uint को मैनेज करने के तरीके का कॉम्बिनेशन है: अरे को फ़्लैट किया गया है और Java अरे की साइन वाली वैल्यू को, रेंडरस्क्रिप्ट की साइन नहीं वाली एलिमेंट वैल्यू के तौर पर समझा जाता है.

ध्यान दें कि तीसरे तरीके के लिए, इनपुट टाइप को नतीजों के टाइप से अलग तरीके से मैनेज किया जाता है:

  • स्क्रिप्ट का वेक्टर इनपुट 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 के सैंपल, इस पेज पर बताए गए एपीआई के इस्तेमाल को और बेहतर तरीके से दिखाते हैं.