রেন্ডারস্ক্রিপ্ট ওভারভিউ

রেন্ডারস্ক্রিপ্ট হল Android-এ উচ্চ কার্যসম্পাদনে গণনামূলকভাবে নিবিড় কাজ চালানোর জন্য একটি কাঠামো। রেন্ডারস্ক্রিপ্ট প্রাথমিকভাবে ডেটা-সমান্তরাল গণনার সাথে ব্যবহারের জন্য ভিত্তিক, যদিও সিরিয়াল ওয়ার্কলোডগুলিও উপকৃত হতে পারে। রেন্ডারস্ক্রিপ্ট রানটাইম একটি ডিভাইসে উপলব্ধ প্রসেসরের কাজকে সমান্তরাল করে, যেমন মাল্টি-কোর সিপিইউ এবং জিপিইউ। এটি আপনাকে কাজের সময় নির্ধারণের পরিবর্তে অ্যালগরিদম প্রকাশের উপর ফোকাস করতে দেয়। রেন্ডারস্ক্রিপ্ট বিশেষ করে ইমেজ প্রসেসিং, কম্পিউটেশনাল ফটোগ্রাফি বা কম্পিউটার ভিশন সম্পাদনকারী অ্যাপ্লিকেশনের জন্য উপযোগী।

রেন্ডারস্ক্রিপ্ট দিয়ে শুরু করার জন্য, আপনার বোঝা উচিত দুটি প্রধান ধারণা:

একটি রেন্ডারস্ক্রিপ্ট কার্নেল লেখা

একটি RenderScript কার্নেল সাধারণত <project_root>/src/rs ডিরেক্টরির একটি .rs ফাইলে থাকে; প্রতিটি .rs ফাইলকে স্ক্রিপ্ট বলা হয়। প্রতিটি স্ক্রিপ্টে নিজস্ব কার্নেল, ফাংশন এবং ভেরিয়েবলের সেট থাকে। একটি স্ক্রিপ্ট থাকতে পারে:

  • একটি প্রাগমা ঘোষণা ( #pragma version(1) ) যা এই স্ক্রিপ্টে ব্যবহৃত RenderScript কার্নেল ভাষার সংস্করণ ঘোষণা করে। বর্তমানে, 1 হল একমাত্র বৈধ মান।
  • একটি প্রাগমা ঘোষণা ( #pragma rs java_package_name(com.example.app) ) যা এই স্ক্রিপ্ট থেকে প্রতিফলিত জাভা ক্লাসের প্যাকেজ নাম ঘোষণা করে। মনে রাখবেন যে আপনার .rs ফাইলটি আপনার অ্যাপ্লিকেশন প্যাকেজের অংশ হতে হবে, এবং একটি লাইব্রেরি প্রকল্পে নয়।
  • শূন্য বা তার বেশি কার্যকরী ফাংশন । একটি আহ্বানযোগ্য ফাংশন হল একটি একক-থ্রেডেড রেন্ডারস্ক্রিপ্ট ফাংশন যা আপনি নির্বিচারে আর্গুমেন্ট সহ আপনার জাভা কোড থেকে কল করতে পারেন। এগুলি প্রায়শই একটি বৃহত্তর প্রক্রিয়াকরণ পাইপলাইনের মধ্যে প্রাথমিক সেটআপ বা সিরিয়াল গণনার জন্য দরকারী।
  • শূন্য বা তার বেশি স্ক্রিপ্ট গ্লোবাল । একটি স্ক্রিপ্ট গ্লোবাল সি-তে একটি গ্লোবাল ভেরিয়েবলের অনুরূপ। আপনি জাভা কোড থেকে স্ক্রিপ্ট গ্লোবাল অ্যাক্সেস করতে পারেন এবং এটি প্রায়শই রেন্ডারস্ক্রিপ্ট কার্নেলে প্যারামিটার পাস করার জন্য ব্যবহৃত হয়। স্ক্রিপ্ট গ্লোবাল এখানে আরো বিস্তারিতভাবে ব্যাখ্যা করা হয়েছে।

  • শূন্য বা তার বেশি কম্পিউট কার্নেল । একটি কম্পিউট কার্নেল হল একটি ফাংশন বা ফাংশনের সংগ্রহ যা আপনি ডেটার সংগ্রহ জুড়ে সমান্তরালভাবে চালানোর জন্য রেন্ডারস্ক্রিপ্ট রানটাইমকে নির্দেশ করতে পারেন। দুই ধরনের কম্পিউট কার্নেল আছে: ম্যাপিং কার্নেল (যাকে ফোরচ কার্নেলও বলা হয়) এবং রিডাকশন কার্নেল।

    একটি ম্যাপিং কার্নেল একটি সমান্তরাল ফাংশন যা একই মাত্রার 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;
      }

      বেশিরভাগ ক্ষেত্রে, এটি একটি স্ট্যান্ডার্ড সি ফাংশনের অনুরূপ। ফাংশন প্রোটোটাইপে প্রয়োগ করা RS_KERNEL প্রপার্টি নির্দিষ্ট করে যে ফাংশনটি একটি রেন্ডারস্ক্রিপ্ট ম্যাপিং কার্নেল একটি ইনভোকেবল ফাংশনের পরিবর্তে। কার্নেল লঞ্চে পাঠানো ইনপুট Allocation উপর ভিত্তি করে in আর্গুমেন্ট স্বয়ংক্রিয়ভাবে পূরণ করা হয়। আর্গুমেন্ট x এবং y নিচে আলোচনা করা হয়েছে। কার্নেল থেকে প্রত্যাবর্তিত মানটি স্বয়ংক্রিয়ভাবে আউটপুট Allocation উপযুক্ত অবস্থানে লেখা হয়। ডিফল্টরূপে, এই কার্নেলটি তার সম্পূর্ণ ইনপুট Allocation জুড়ে চালিত হয়, Allocation Element প্রতি কার্নেল ফাংশনের একটি সম্পাদনের সাথে।

      একটি ম্যাপিং কার্নেলে এক বা একাধিক ইনপুট Allocations , একটি একক আউটপুট Allocation বা উভয়ই থাকতে পারে। রেন্ডারস্ক্রিপ্ট রানটাইম চেক করে যে সমস্ত ইনপুট এবং আউটপুট বরাদ্দকরণের একই মাত্রা রয়েছে এবং ইনপুট এবং আউটপুট বরাদ্দের Element ধরনগুলি কার্নেলের প্রোটোটাইপের সাথে মেলে; যদি এই চেকগুলির মধ্যে একটি ব্যর্থ হয়, রেন্ডারস্ক্রিপ্ট একটি ব্যতিক্রম নিক্ষেপ করে।

      দ্রষ্টব্য: Android 6.0 (API স্তর 23) এর আগে, একটি ম্যাপিং কার্নেলে একাধিক ইনপুট Allocation নাও থাকতে পারে।

      আপনার যদি কার্নেলের চেয়ে বেশি ইনপুট বা আউটপুট Allocations প্রয়োজন হয়, সেই বস্তুগুলিকে rs_allocation স্ক্রিপ্ট গ্লোবালের সাথে আবদ্ধ করা উচিত এবং rsGetElementAt_ type () বা rsSetElementAt_ type () এর মাধ্যমে একটি কার্নেল বা ইনভোকেবল ফাংশন থেকে অ্যাক্সেস করা উচিত।

      দ্রষ্টব্য: RS_KERNEL হল আপনার সুবিধার জন্য RenderScript দ্বারা স্বয়ংক্রিয়ভাবে সংজ্ঞায়িত একটি ম্যাক্রো:

      #define RS_KERNEL __attribute__((kernel))

    একটি হ্রাস কার্নেল হল ফাংশনের একটি পরিবার যা একই মাত্রার ইনপুট Allocations সংগ্রহের উপর কাজ করে। ডিফল্টরূপে, এর সঞ্চয়কারী ফাংশন ঐ মাত্রার প্রতিটি স্থানাঙ্কের জন্য একবার কার্যকর করে। এটি সাধারণত (কিন্তু একচেটিয়াভাবে নয়) একটি একক মানের ইনপুট 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 , এই উদাহরণে) হল একটি সঞ্চয়কারী ডেটা আইটেমের একটি পয়েন্টার এবং দ্বিতীয়টি ( val , এই উদাহরণে) কার্নেল লঞ্চে পাস করা ইনপুট Allocation উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে পূরণ করা হয়। রেন্ডারস্ক্রিপ্ট রানটাইম দ্বারা সঞ্চয়কারী ডেটা আইটেম তৈরি করা হয়; ডিফল্টরূপে, এটি শূন্য থেকে শুরু হয়। ডিফল্টরূপে, এই কার্নেলটি তার সম্পূর্ণ ইনপুট Allocation জুড়ে চালিত হয়, Allocation Element প্রতি অ্যাকিউমুলেটর ফাংশনের একটি সম্পাদনের সাথে। ডিফল্টরূপে, সঞ্চয়কারী ডেটা আইটেমের চূড়ান্ত মান হ্রাসের ফলাফল হিসাবে বিবেচিত হয় এবং জাভাতে ফেরত দেওয়া হয়। রেন্ডারস্ক্রিপ্ট রানটাইম ইনপুট বরাদ্দকরণের Element ধরণটি সঞ্চয়কারী ফাংশনের প্রোটোটাইপের সাথে মেলে তা নিশ্চিত করতে পরীক্ষা করে; যদি এটি মেলে না, রেন্ডারস্ক্রিপ্ট একটি ব্যতিক্রম নিক্ষেপ করে।

      একটি রিডাকশন কার্নেলে এক বা একাধিক ইনপুট Allocations থাকে কিন্তু কোনো আউটপুট Allocations থাকে না।

      রিডাকশন কার্নেল এখানে আরো বিস্তারিতভাবে ব্যাখ্যা করা হয়েছে।

      হ্রাস কার্নেলগুলি Android 7.0 (API স্তর 24) এবং পরবর্তীতে সমর্থিত।

    একটি ম্যাপিং কার্নেল ফাংশন বা একটি হ্রাস কার্নেল সঞ্চয়কারী ফাংশন বিশেষ আর্গুমেন্ট x , y , এবং z ব্যবহার করে বর্তমান এক্সিকিউশনের স্থানাঙ্কগুলি অ্যাক্সেস করতে পারে, যা অবশ্যই int বা uint32_t টাইপের হতে হবে। এই আর্গুমেন্ট ঐচ্ছিক.

    একটি ম্যাপিং কার্নেল ফাংশন বা একটি রিডাকশন কার্নেল অ্যাকুমুলেটর ফাংশন rs_kernel_context টাইপের ঐচ্ছিক বিশেষ আর্গুমেন্ট context নিতে পারে। এটি রানটাইম API-এর একটি পরিবারের দ্বারা প্রয়োজন যা বর্তমান সম্পাদনের নির্দিষ্ট বৈশিষ্ট্যগুলি অনুসন্ধান করতে ব্যবহৃত হয় -- উদাহরণস্বরূপ, rsGetDimX । ( context যুক্তিটি Android 6.0 (API স্তর 23) এবং পরবর্তীতে উপলব্ধ।)

  • একটি ঐচ্ছিক init() ফাংশন। init() ফাংশন হল একটি বিশেষ ধরনের ইনভোকেবল ফাংশন যা রেন্ডারস্ক্রিপ্ট রান করে যখন স্ক্রিপ্টটি প্রথম ইনস্ট্যান্ট করা হয়। এটি স্ক্রিপ্ট তৈরিতে স্বয়ংক্রিয়ভাবে কিছু গণনার জন্য অনুমতি দেয়।
  • শূন্য বা তার বেশি স্ট্যাটিক স্ক্রিপ্ট গ্লোবাল এবং ফাংশন । একটি স্ট্যাটিক স্ক্রিপ্ট গ্লোবাল একটি স্ক্রিপ্ট গ্লোবালের সমতুল্য তবে এটি জাভা কোড থেকে অ্যাক্সেস করা যায় না। একটি স্ট্যাটিক ফাংশন হল একটি স্ট্যান্ডার্ড সি ফাংশন যা স্ক্রিপ্টের যেকোন কার্নেল বা ইনভোকেবল ফাংশন থেকে কল করা যেতে পারে কিন্তু জাভা API এর সংস্পর্শে আসে না। যদি জাভা কোড থেকে কোনো স্ক্রিপ্ট গ্লোবাল বা ফাংশন অ্যাক্সেস করার প্রয়োজন না হয়, তবে এটিকে static ঘোষণা করার জন্য অত্যন্ত সুপারিশ করা হয়।

ফ্লোটিং পয়েন্ট নির্ভুলতা সেট করা

আপনি একটি স্ক্রিপ্টে ফ্লোটিং পয়েন্ট নির্ভুলতার প্রয়োজনীয় স্তর নিয়ন্ত্রণ করতে পারেন। এটি কার্যকর যদি সম্পূর্ণ IEEE 754-2008 মান (ডিফল্টরূপে ব্যবহৃত) প্রয়োজন না হয়। নিম্নলিখিত pragmas ফ্লোটিং পয়েন্ট নির্ভুলতার একটি ভিন্ন স্তর সেট করতে পারে:

  • #pragma rs_fp_full (কিছু নির্দিষ্ট না থাকলে ডিফল্ট): যে অ্যাপগুলির জন্য IEEE 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 CPU নির্দেশাবলী)।

জাভা থেকে রেন্ডারস্ক্রিপ্ট এপিআই অ্যাক্সেস করা

রেন্ডারস্ক্রিপ্ট ব্যবহার করে এমন একটি অ্যান্ড্রয়েড অ্যাপ্লিকেশন ডেভেলপ করার সময়, আপনি দুটি উপায়ে জাভা থেকে এর API অ্যাক্সেস করতে পারেন:

  • android.renderscript - এই ক্লাস প্যাকেজের APIগুলি Android 3.0 (API স্তর 11) এবং উচ্চতর চলমান ডিভাইসগুলিতে উপলব্ধ।
  • android.support.v8.renderscript - এই প্যাকেজের APIগুলি একটি সাপোর্ট লাইব্রেরির মাধ্যমে উপলব্ধ, যা আপনাকে Android 2.3 (API স্তর 9) এবং উচ্চতর চলমান ডিভাইসগুলিতে সেগুলি ব্যবহার করতে দেয়৷

এখানে ট্রেডঅফ রয়েছে:

  • আপনি যদি সাপোর্ট লাইব্রেরি API ব্যবহার করেন, তাহলে আপনার অ্যাপ্লিকেশনের রেন্ডারস্ক্রিপ্ট অংশটি Android 2.3 (API স্তর 9) এবং উচ্চতর চলমান ডিভাইসগুলির সাথে সামঞ্জস্যপূর্ণ হবে, আপনি যে রেন্ডারস্ক্রিপ্ট বৈশিষ্ট্যগুলি ব্যবহার করেন তা নির্বিশেষে৷ আপনি যদি নেটিভ ( android.renderscript ) API ব্যবহার করেন তবে এটি আপনার অ্যাপ্লিকেশনটিকে আরও বেশি ডিভাইসে কাজ করার অনুমতি দেয়৷
  • কিছু রেন্ডারস্ক্রিপ্ট বৈশিষ্ট্য সমর্থন লাইব্রেরি API-এর মাধ্যমে উপলব্ধ নয়।
  • আপনি যদি সাপোর্ট লাইব্রেরি API ব্যবহার করেন, তাহলে আপনি নেটিভ ( android.renderscript ) API ব্যবহার করার চেয়ে (সম্ভবত উল্লেখযোগ্যভাবে) বড় APK পাবেন।

রেন্ডারস্ক্রিপ্ট সাপোর্ট লাইব্রেরি এপিআই ব্যবহার করা

সাপোর্ট লাইব্রেরি রেন্ডারস্ক্রিপ্ট এপিআই ব্যবহার করার জন্য, আপনাকে অবশ্যই আপনার ডেভেলপমেন্ট এনভায়রনমেন্ট কনফিগার করতে হবে যাতে সেগুলি অ্যাক্সেস করা যায়। এই APIগুলি ব্যবহার করার জন্য নিম্নলিখিত Android SDK সরঞ্জামগুলির প্রয়োজন:

  • Android SDK টুলস রিভিশন 22.2 বা উচ্চতর
  • Android SDK বিল্ড-টুলস রিভিশন 18.1.0 বা তার বেশি

মনে রাখবেন যে Android SDK বিল্ড-টুল 24.0.0 থেকে শুরু করে, Android 2.2 (API লেভেল 8) আর সমর্থিত নয়।

আপনি Android SDK ম্যানেজারে এই টুলগুলির ইনস্টল করা সংস্করণটি পরীক্ষা করতে এবং আপডেট করতে পারেন৷

সাপোর্ট লাইব্রেরি রেন্ডারস্ক্রিপ্ট API ব্যবহার করতে:

  1. নিশ্চিত করুন যে আপনার প্রয়োজনীয় Android SDK সংস্করণ ইনস্টল করা আছে।
  2. রেন্ডারস্ক্রিপ্ট সেটিংস অন্তর্ভুক্ত করতে Android বিল্ড প্রক্রিয়ার সেটিংস আপডেট করুন:
    • আপনার অ্যাপ্লিকেশন মডিউলের অ্যাপ ফোল্ডারে build.gradle ফাইলটি খুলুন।
    • ফাইলটিতে নিম্নলিখিত রেন্ডারস্ক্রিপ্ট সেটিংস যোগ করুন:

      গ্রোভি

              android {
                  compileSdkVersion 33
      
                  defaultConfig {
                      minSdkVersion 9
                      targetSdkVersion 19
      
                      renderscriptTargetApi 18
                      renderscriptSupportModeEnabled true
                  }
              }
              

      কোটলিন

              android {
                  compileSdkVersion(33)
      
                  defaultConfig {
                      minSdkVersion(9)
                      targetSdkVersion(19)
      
                      renderscriptTargetApi = 18
                      renderscriptSupportModeEnabled = true
                  }
              }
              

      উপরে তালিকাভুক্ত সেটিংস অ্যান্ড্রয়েড বিল্ড প্রক্রিয়ায় নির্দিষ্ট আচরণ নিয়ন্ত্রণ করে:

      • renderscriptTargetApi - তৈরি করা বাইটকোড সংস্করণ নির্দিষ্ট করে। আমরা সুপারিশ করি যে আপনি এই মানটিকে সর্বনিম্ন API স্তরে সেট করুন যা আপনি ব্যবহার করছেন এমন সমস্ত কার্যকারিতা প্রদান করতে সক্ষম এবং renderscriptSupportModeEnabled কে true সেট করুন। এই সেটিং এর বৈধ মান হল 11 থেকে অতি সম্প্রতি প্রকাশিত API স্তর পর্যন্ত যেকোনো পূর্ণসংখ্যার মান। আপনার অ্যাপ্লিকেশান ম্যানিফেস্টে নির্দিষ্ট করা আপনার ন্যূনতম SDK সংস্করণটি একটি ভিন্ন মান সেট করা থাকলে, সেই মানটিকে উপেক্ষা করা হয় এবং বিল্ড ফাইলের লক্ষ্য মানটি ন্যূনতম SDK সংস্করণ সেট করতে ব্যবহৃত হয়৷
      • renderscriptSupportModeEnabled - নির্দিষ্ট করে যে জেনারেট করা বাইটকোডটি একটি সামঞ্জস্যপূর্ণ সংস্করণে ফিরে আসা উচিত যদি এটি যে ডিভাইসে চলছে সেটি লক্ষ্য সংস্করণটিকে সমর্থন না করে।
  3. আপনার অ্যাপ্লিকেশন ক্লাসে যেগুলি রেন্ডারস্ক্রিপ্ট ব্যবহার করে, সমর্থন লাইব্রেরি ক্লাসগুলির জন্য একটি আমদানি যোগ করুন:

    কোটলিন

    import android.support.v8.renderscript.*

    জাভা

    import android.support.v8.renderscript.*;

জাভা বা কোটলিন কোড থেকে রেন্ডারস্ক্রিপ্ট ব্যবহার করা

Java বা Kotlin কোড থেকে RenderScript ব্যবহার করা android.renderscript বা android.support.v8.renderscript প্যাকেজে অবস্থিত API ক্লাসের উপর নির্ভর করে। বেশিরভাগ অ্যাপ্লিকেশন একই মৌলিক ব্যবহার প্যাটার্ন অনুসরণ করে:

  1. একটি রেন্ডারস্ক্রিপ্ট প্রসঙ্গ শুরু করুন। RenderScript কনটেক্সট, create(Context) দিয়ে তৈরি, নিশ্চিত করে যে RenderScript ব্যবহার করা যেতে পারে এবং পরবর্তী সমস্ত RenderScript অবজেক্টের জীবনকাল নিয়ন্ত্রণ করতে একটি অবজেক্ট প্রদান করে। আপনার প্রসঙ্গ তৈরিকে একটি সম্ভাব্য দীর্ঘমেয়াদী অপারেশন হিসাবে বিবেচনা করা উচিত, কারণ এটি হার্ডওয়্যারের বিভিন্ন অংশে সংস্থান তৈরি করতে পারে; যদি সম্ভব হয় তবে এটি একটি অ্যাপ্লিকেশনের সমালোচনামূলক পথে থাকা উচিত নয়। সাধারণত, একটি অ্যাপ্লিকেশনে একটি সময়ে শুধুমাত্র একটি রেন্ডারস্ক্রিপ্ট প্রসঙ্গ থাকবে।
  2. একটি স্ক্রিপ্টে পাস করার জন্য কমপক্ষে একটি Allocation তৈরি করুন৷ একটি Allocation একটি রেন্ডারস্ক্রিপ্ট অবজেক্ট যা একটি নির্দিষ্ট পরিমাণ ডেটার জন্য স্টোরেজ প্রদান করে। স্ক্রিপ্টের কার্নেলগুলি Allocation বস্তুগুলিকে তাদের ইনপুট এবং আউটপুট হিসাবে নেয় এবং স্ক্রিপ্ট গ্লোবাল হিসাবে আবদ্ধ থাকাকালীন rsGetElementAt_ type () এবং rsSetElementAt_ type () ব্যবহার করে Allocation বস্তুগুলি কার্নেলে অ্যাক্সেস করা যেতে পারে। Allocation অবজেক্ট অ্যারেগুলিকে জাভা কোড থেকে রেন্ডারস্ক্রিপ্ট কোডে পাস করার অনুমতি দেয় এবং এর বিপরীতে। Allocation বস্তুগুলি সাধারণত createTyped() বা createFromBitmap() ব্যবহার করে তৈরি করা হয়।
  3. প্রয়োজনীয় স্ক্রিপ্ট তৈরি করুন। রেন্ডারস্ক্রিপ্ট ব্যবহার করার সময় আপনার কাছে দুটি ধরণের স্ক্রিপ্ট উপলব্ধ রয়েছে:
    • স্ক্রিপ্টসি : উপরে রেন্ডারস্ক্রিপ্ট কার্নেল লেখায় বর্ণিত ব্যবহারকারী-সংজ্ঞায়িত স্ক্রিপ্টগুলি। প্রতিটি স্ক্রিপ্টে জাভা কোড থেকে স্ক্রিপ্ট অ্যাক্সেস করা সহজ করার জন্য রেন্ডারস্ক্রিপ্ট কম্পাইলার দ্বারা প্রতিফলিত একটি জাভা ক্লাস রয়েছে; এই শ্রেণীর নাম ScriptC_ filename নাম আছে। উদাহরণস্বরূপ, যদি উপরের ম্যাপিং কার্নেলটি invert.rs এ অবস্থিত হয় এবং একটি RenderScript প্রসঙ্গ ইতিমধ্যে mRenderScript এ অবস্থিত থাকে, তাহলে স্ক্রিপ্টটি ইনস্ট্যান্টিয়েট করার জন্য জাভা বা কোটলিন কোড হবে:

      কোটলিন

      val invert = ScriptC_invert(renderScript)

      জাভা

      ScriptC_invert invert = new ScriptC_invert(renderScript);
    • ScriptIntrinsic : এগুলি সাধারণ ক্রিয়াকলাপের জন্য অন্তর্নির্মিত রেন্ডারস্ক্রিপ্ট কার্নেল, যেমন গাউসিয়ান ব্লার, কনভোলিউশন এবং ইমেজ ব্লেন্ডিং। আরও তথ্যের জন্য, ScriptIntrinsic এর সাবক্লাসগুলি দেখুন।
  4. ডেটা দিয়ে বরাদ্দ করা। createFromBitmap() দিয়ে তৈরি করা বরাদ্দ ব্যতীত, একটি বরাদ্দ যখন প্রথম তৈরি করা হয় তখন খালি ডেটা দিয়ে তৈরি করা হয়। একটি বরাদ্দ তৈরি করতে, Allocation "কপি" পদ্ধতিগুলির একটি ব্যবহার করুন৷ "কপি" পদ্ধতিগুলি সিঙ্ক্রোনাস
  5. যেকোনো প্রয়োজনীয় স্ক্রিপ্ট গ্লোবাল সেট করুন। আপনি set_ globalname নামের একই ScriptC_ filename ক্লাসে পদ্ধতি ব্যবহার করে গ্লোবাল সেট করতে পারেন। উদাহরণস্বরূপ, threshold নামের একটি int ভেরিয়েবল সেট করতে, জাভা পদ্ধতি ব্যবহার করুন set_threshold(int) ; এবং lookup নামের একটি rs_allocation ভেরিয়েবল সেট করতে, Java মেথড set_lookup(Allocation) ব্যবহার করুন। set পদ্ধতিগুলি অ্যাসিঙ্ক্রোনাস
  6. উপযুক্ত কার্নেল এবং অনিবার্য ফাংশন চালু করুন।

    একটি প্রদত্ত কার্নেল চালু করার পদ্ধতিগুলি একই ScriptC_ filename ক্লাসে প্রতিফলিত হয় যার নাম forEach_ mappingKernelName () বা reduce_ reductionKernelName () । এই লঞ্চগুলি অ্যাসিঙ্ক্রোনাস । কার্নেলের আর্গুমেন্টের উপর নির্ভর করে, পদ্ধতিটি এক বা একাধিক বরাদ্দ নেয়, যার সবকটির অবশ্যই একই মাত্রা থাকতে হবে। ডিফল্টরূপে, একটি কার্নেল ঐ মাত্রার প্রতিটি স্থানাঙ্কের উপর কার্যকর করে; এই স্থানাঙ্কগুলির একটি উপসেটের উপর একটি কার্নেল চালানোর জন্য, একটি উপযুক্ত Script.LaunchOptions forEach বা reduce পদ্ধতির শেষ যুক্তি হিসাবে পাস করুন।

    একই ScriptC_ filename ক্লাসে প্রতিফলিত invoke_ functionName পদ্ধতিগুলি ব্যবহার করে আমন্ত্রণযোগ্য ফাংশন চালু করুন। এই লঞ্চগুলি অ্যাসিঙ্ক্রোনাস

  7. Allocation বস্তু এবং javaFutureType অবজেক্ট থেকে ডেটা পুনরুদ্ধার করুন। জাভা কোড থেকে একটি Allocation থেকে ডেটা অ্যাক্সেস করার জন্য, আপনাকে অবশ্যই Allocation "অনুলিপি" পদ্ধতিগুলির একটি ব্যবহার করে সেই ডেটা জাভাতে কপি করতে হবে। একটি হ্রাস কার্নেলের ফলাফল পেতে, আপনাকে অবশ্যই javaFutureType .get() পদ্ধতি ব্যবহার করতে হবে। "কপি" এবং get() পদ্ধতিগুলি সিঙ্ক্রোনাস
  8. রেন্ডারস্ক্রিপ্ট প্রসঙ্গ ছিঁড়ে ফেলুন। আপনি destroy() দিয়ে RenderScript প্রসঙ্গ ধ্বংস করতে পারেন অথবা RenderScript প্রসঙ্গ অবজেক্টকে আবর্জনা সংগ্রহ করার অনুমতি দিয়ে। এটি একটি ব্যতিক্রম নিক্ষেপ করতে সেই প্রসঙ্গের সাথে সম্পর্কিত যেকোন বস্তুর আরও ব্যবহার ঘটায়।

অ্যাসিঙ্ক্রোনাস এক্সিকিউশন মডেল

প্রতিফলিত forEach , invoke , reduce , এবং set পদ্ধতিগুলি অ্যাসিঙ্ক্রোনাস -- প্রতিটি অনুরোধ করা ক্রিয়া শেষ করার আগে জাভাতে ফিরে আসতে পারে৷ যাইহোক, স্বতন্ত্র ক্রিয়াগুলি সেগুলি যে ক্রমানুসারে চালু করা হয় সে অনুসারে ক্রমিক করা হয়।

Allocation শ্রেণী বরাদ্দ এবং থেকে ডেটা অনুলিপি করার জন্য "কপি" পদ্ধতি সরবরাহ করে। একটি "অনুলিপি" পদ্ধতি সিঙ্ক্রোনাস, এবং একই বরাদ্দকে স্পর্শ করে এমন উপরের যেকোনও অ্যাসিঙ্ক্রোনাস অ্যাকশনের ক্ষেত্রে ক্রমিক করা হয়।

প্রতিফলিত javaFutureType ক্লাসগুলি হ্রাসের ফলাফল পেতে একটি get() পদ্ধতি প্রদান করে। get() সিঙ্ক্রোনাস, এবং হ্রাসের সাথে সিরিয়াল করা হয় (যা অ্যাসিঙ্ক্রোনাস)।

একক-উৎস রেন্ডারস্ক্রিপ্ট

অ্যান্ড্রয়েড 7.0 (এপিআই লেভেল 24) সিঙ্গেল-সোর্স রেন্ডারস্ক্রিপ্ট নামে একটি নতুন প্রোগ্রামিং বৈশিষ্ট্য প্রবর্তন করে, যেখানে কার্নেলগুলি জাভা থেকে না হয়ে স্ক্রিপ্ট থেকে শুরু করা হয় যেখানে তারা সংজ্ঞায়িত করা হয়। এই পদ্ধতিটি বর্তমানে ম্যাপিং কার্নেলের মধ্যে সীমাবদ্ধ, যা সংক্ষিপ্ততার জন্য এই বিভাগে কেবল "কার্নেল" হিসাবে উল্লেখ করা হয়েছে। এই নতুন বৈশিষ্ট্যটি স্ক্রিপ্টের ভিতরে থেকে rs_allocation প্রকারের বরাদ্দ তৈরি করতেও সমর্থন করে। একাধিক কার্নেল লঞ্চের প্রয়োজন হলেও শুধুমাত্র একটি স্ক্রিপ্টের মধ্যে সম্পূর্ণ অ্যালগরিদম বাস্তবায়ন করা এখন সম্ভব। সুবিধাটি দ্বিগুণ: আরও পাঠযোগ্য কোড, কারণ এটি একটি ভাষায় একটি অ্যালগরিদম বাস্তবায়ন রাখে; এবং সম্ভাব্য দ্রুত কোড, কারণ একাধিক কার্নেল লঞ্চ জুড়ে জাভা এবং রেন্ডারস্ক্রিপ্টের মধ্যে কম ট্রানজিশন।

একক-উৎস রেন্ডারস্ক্রিপ্টে, আপনি একটি রেন্ডারস্ক্রিপ্ট কার্নেল লেখায় বর্ণিত কার্নেলগুলি লেখেন। তারপরে আপনি একটি অনিবার্য ফাংশন লিখুন যা তাদের চালু করতে rsForEach() কল করে। সেই API প্রথম প্যারামিটার হিসাবে একটি কার্নেল ফাংশন নেয়, তারপরে ইনপুট এবং আউটপুট বরাদ্দ করে। একটি অনুরূপ API rsForEachWithOptions() rs_script_call_t প্রকারের একটি অতিরিক্ত আর্গুমেন্ট নেয়, যা কার্নেল ফাংশন প্রক্রিয়া করার জন্য ইনপুট এবং আউটপুট বরাদ্দ থেকে উপাদানগুলির একটি উপসেট নির্দিষ্ট করে।

রেন্ডারস্ক্রিপ্ট গণনা শুরু করতে, আপনি জাভা থেকে আহ্বানযোগ্য ফাংশনকে কল করুন। জাভা কোড থেকে রেন্ডারস্ক্রিপ্ট ব্যবহার করার ধাপগুলি অনুসরণ করুন। ধাপে উপযুক্ত কার্নেল চালু করুন , invoke_ function_name () ব্যবহার করে ইনভোকেবল ফাংশনটি কল করুন, যা কার্নেল চালু করা সহ পুরো গণনা শুরু করবে।

একটি কার্নেল লঞ্চ থেকে অন্য কার্নেল লঞ্চে মধ্যবর্তী ফলাফল সংরক্ষণ এবং পাস করার জন্য প্রায়শই বরাদ্দের প্রয়োজন হয়। আপনি rsCreateAllocation() ব্যবহার করে এগুলি তৈরি করতে পারেন। সেই API-এর একটি সহজ-ব্যবহারযোগ্য ফর্ম হল rsCreateAllocation_<T><W>(…) , যেখানে T হল একটি উপাদানের ডেটা টাইপ এবং W হল উপাদানটির ভেক্টর প্রস্থ। এপিআই আর্গুমেন্ট হিসাবে X, Y, এবং Z আকারের আকার নেয়। 1D বা 2D বরাদ্দের জন্য, Y বা Z মাত্রার আকার বাদ দেওয়া যেতে পারে। উদাহরণস্বরূপ, rsCreateAllocation_uchar4(16384) 16384টি উপাদানের একটি 1D বরাদ্দ তৈরি করে, যার প্রত্যেকটি uchar4 টাইপের।

বরাদ্দ স্বয়ংক্রিয়ভাবে সিস্টেম দ্বারা পরিচালিত হয়. আপনি স্পষ্টভাবে তাদের মুক্তি বা মুক্ত করতে হবে না. যাইহোক, আপনি rsClearObject(rs_allocation* alloc) কল করতে পারেন নির্দেশ করতে যে আপনার আর অন্তর্নিহিত বরাদ্দের জন্য হ্যান্ডেল alloc প্রয়োজন নেই, যাতে সিস্টেম যত তাড়াতাড়ি সম্ভব রিসোর্স খালি করতে পারে।

একটি রেন্ডারস্ক্রিপ্ট কার্নেল লেখা বিভাগে একটি উদাহরণ কার্নেল রয়েছে যা একটি চিত্রকে উল্টে দেয়। নীচের উদাহরণটি প্রসারিত করে যে একক-উৎস রেন্ডারস্ক্রিপ্ট ব্যবহার করে একটি ছবিতে একাধিক প্রভাব প্রয়োগ করতে। এটিতে আরেকটি কার্নেল রয়েছে, 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);
}

আপনি জাভা বা কোটলিন থেকে process() ফাংশনটিকে নিম্নরূপ কল করতে পারেন:

কোটলিন

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

এই উদাহরণটি দেখায় কিভাবে একটি অ্যালগরিদম যাতে দুটি কার্নেল লঞ্চ জড়িত থাকে তা সম্পূর্ণরূপে রেন্ডারস্ক্রিপ্ট ভাষাতেই প্রয়োগ করা যেতে পারে। একক-উৎস রেন্ডারস্ক্রিপ্ট ছাড়া, আপনাকে জাভা কোড থেকে উভয় কার্নেল চালু করতে হবে, কার্নেল সংজ্ঞা থেকে কার্নেল লঞ্চগুলিকে আলাদা করে এবং পুরো অ্যালগরিদম বোঝা কঠিন করে তোলে। শুধুমাত্র একক-উৎস রেন্ডারস্ক্রিপ্ট কোড পড়া সহজ নয়, এটি কার্নেল লঞ্চ জুড়ে জাভা এবং স্ক্রিপ্টের মধ্যে রূপান্তরকেও দূর করে। কিছু পুনরাবৃত্তিমূলক অ্যালগরিদম শত শত বার কার্নেল চালু করতে পারে, যা এই ধরনের ট্রানজিশনের ওভারহেডকে উল্লেখযোগ্য করে তোলে।

স্ক্রিপ্ট গ্লোবাল

একটি স্ক্রিপ্ট গ্লোবাল একটি স্ক্রিপ্ট ( .rs ) ফাইলে একটি সাধারণ অ- static গ্লোবাল ভেরিয়েবল। একটি স্ক্রিপ্ট গ্লোবাল নামের var নামক ফাইল ফাইলের filename .rs এ সংজ্ঞায়িত করা হয়েছে, সেখানে একটি পদ্ধতি থাকবে get_ var ScriptC_ filename ক্লাসে প্রতিফলিত হবে। গ্লোবাল const না হলে, একটি পদ্ধতি set_ var ও থাকবে।

একটি প্রদত্ত স্ক্রিপ্ট গ্লোবালের দুটি পৃথক মান রয়েছে - একটি জাভা মান এবং একটি স্ক্রিপ্ট মান। এই মানগুলি নিম্নরূপ আচরণ করে:

  • স্ক্রিপ্টে var-এর একটি স্ট্যাটিক ইনিশিয়ালাইজার থাকলে, এটি জাভা এবং স্ক্রিপ্ট উভয় ক্ষেত্রেই var- এর প্রাথমিক মান নির্দিষ্ট করে। অন্যথায়, সেই প্রাথমিক মানটি শূন্য।
  • স্ক্রিপ্টের মধ্যে var- এর অ্যাক্সেসগুলি এর স্ক্রিপ্ট মান পড়তে এবং লিখতে পারে।
  • get_ var পদ্ধতি জাভা মান পড়ে।
  • set_ var পদ্ধতি (যদি এটি বিদ্যমান থাকে) অবিলম্বে জাভা মান লিখে, এবং স্ক্রিপ্ট মান অসিঙ্ক্রোনাসভাবে লেখে।

দ্রষ্টব্য: এর মানে হল স্ক্রিপ্টের যেকোনো স্ট্যাটিক ইনিশিয়ালাইজার ছাড়া, স্ক্রিপ্টের মধ্যে থেকে বিশ্বব্যাপী লেখা মান জাভাতে দৃশ্যমান নয়।

গভীরতা হ্রাস কার্নেল

রিডাকশন হল ডেটার সংগ্রহকে একটি একক মানের মধ্যে একত্রিত করার প্রক্রিয়া। এটি সমান্তরাল প্রোগ্রামিং-এ একটি দরকারী আদিম, নিম্নলিখিতগুলির মতো অ্যাপ্লিকেশন সহ:

  • সমস্ত ডেটার উপর যোগফল বা পণ্য গণনা করা
  • কম্পিউটিং লজিক্যাল অপারেশন ( and , or , xor ) সমস্ত ডেটার উপর
  • ডেটার মধ্যে সর্বনিম্ন বা সর্বোচ্চ মান খুঁজে বের করা
  • একটি নির্দিষ্ট মান বা ডেটার মধ্যে একটি নির্দিষ্ট মানের স্থানাঙ্কের জন্য অনুসন্ধান করা

অ্যান্ড্রয়েড 7.0 (এপিআই স্তর 24) এবং পরবর্তীতে, রেন্ডারস্ক্রিপ্ট দক্ষ ব্যবহারকারী-লিখিত হ্রাস অ্যালগরিদমগুলিকে অনুমতি দেওয়ার জন্য হ্রাস কার্নেলগুলিকে সমর্থন করে। আপনি 1, 2, বা 3 মাত্রা সহ ইনপুটগুলিতে হ্রাস কার্নেল চালু করতে পারেন।

উপরের একটি উদাহরণ একটি সহজ সংযোজন হ্রাস কার্নেল দেখায়। এখানে একটি আরও জটিল FindMinAndMax হ্রাস কার্নেল রয়েছে যা 1-মাত্রিক Allocation মধ্যে সর্বনিম্ন এবং সর্বোচ্চ long মানের অবস্থানগুলি খুঁজে পায়:

#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;
}

দ্রষ্টব্য: এখানে আরও উদাহরণ হ্রাস কার্নেল রয়েছে।

একটি রিডাকশন কার্নেল চালানোর জন্য, রেন্ডারস্ক্রিপ্ট রানটাইম এক বা একাধিক ভেরিয়েবল তৈরি করে যাকে সঞ্চয়কারী ডেটা আইটেম বলা হয় হ্রাস প্রক্রিয়ার অবস্থা ধরে রাখতে। রেন্ডারস্ক্রিপ্ট রানটাইম এমনভাবে সঞ্চয়কারী ডেটা আইটেমের সংখ্যা বাছাই করে যাতে কর্মক্ষমতা সর্বাধিক হয়। অ্যাকিউমুলেটর ডেটা আইটেমগুলির ধরন ( accumType ) কার্নেলের সঞ্চয়কারী ফাংশন দ্বারা নির্ধারিত হয় -- সেই ফাংশনের প্রথম যুক্তি হল একটি সঞ্চয়কারী ডেটা আইটেমের একটি নির্দেশক৷ ডিফল্টরূপে, প্রতিটি সঞ্চয়কারী ডেটা আইটেম শূন্য থেকে শুরু হয় (যেমন memset দ্বারা); যাইহোক, আপনি ভিন্ন কিছু করার জন্য একটি ইনিশিয়ালাইজার ফাংশন লিখতে পারেন।

উদাহরণ: অ্যাডিন্ট কার্নেলে, ইনপুট মান যোগ করার জন্য অ্যাকিউমুলেটর ডেটা আইটেম ( int টাইপ) ব্যবহার করা হয়। কোন ইনিশিয়ালাইজার ফাংশন নেই, তাই প্রতিটি সঞ্চয়কারী ডেটা আইটেম শূন্য থেকে শুরু হয়।

উদাহরণ: FindMinAndMax কার্নেলে, অ্যাকিউমুলেটর ডেটা আইটেমগুলি (টাইপ MinAndMax ) এ পর্যন্ত পাওয়া ন্যূনতম এবং সর্বাধিক মানগুলির ট্র্যাক রাখতে ব্যবহৃত হয়। এগুলিকে যথাক্রমে LONG_MAX এবং LONG_MIN এ সেট করার জন্য একটি ইনিশিয়ালাইজার ফাংশন রয়েছে; এবং এই মানগুলির অবস্থানগুলি -1 তে সেট করতে, ইঙ্গিত করে যে মানগুলি প্রক্রিয়াকৃত ইনপুটের (খালি) অংশে আসলে উপস্থিত নেই।

রেন্ডারস্ক্রিপ্ট ইনপুট(গুলি) প্রতিটি স্থানাঙ্কের জন্য একবার আপনার সঞ্চয়কারী ফাংশনকে কল করে। সাধারণত, আপনার ফাংশন ইনপুট অনুযায়ী কিছু উপায়ে সঞ্চয়কারী ডেটা আইটেম আপডেট করা উচিত।

উদাহরণ: অ্যাডিন্ট কার্নেলে, অ্যাকিউমুলেটর ফাংশন একটি ইনপুট উপাদানের মান সঞ্চয়কারী ডেটা আইটেমে যোগ করে।

উদাহরণ: FindMinAndMax কার্নেলে, সঞ্চয়কারী ফাংশন একটি ইনপুট উপাদানের মান সঞ্চয়কারী ডেটা আইটেমে রেকর্ড করা ন্যূনতম মানের থেকে কম বা সমান এবং/অথবা সঞ্চয়কারীতে রেকর্ড করা সর্বোচ্চ মানের চেয়ে বেশি বা সমান কিনা তা পরীক্ষা করে। ডেটা আইটেম, এবং সেই অনুযায়ী সঞ্চয়কারী ডেটা আইটেম আপডেট করে।

ইনপুট(গুলি) মধ্যে প্রতিটি স্থানাঙ্কের জন্য একুমুলেটর ফাংশন একবার কল করার পরে, রেন্ডারস্ক্রিপ্টকে অবশ্যই সঞ্চয়কারী ডেটা আইটেমগুলিকে একটি একক সঞ্চয়কারী ডেটা আইটেমে একত্রিত করতে হবে। আপনি এটি করতে একটি কম্বাইনার ফাংশন লিখতে পারেন। যদি সঞ্চয়কারী ফাংশনে একটি একক ইনপুট থাকে এবং কোনো বিশেষ আর্গুমেন্ট না থাকে, তাহলে আপনাকে একটি কম্বাইনার ফাংশন লিখতে হবে না; রেন্ডারস্ক্রিপ্ট অ্যাকিউমুলেটর ডেটা আইটেমগুলিকে একত্রিত করতে সঞ্চয়কারী ফাংশন ব্যবহার করবে। (যদি এই ডিফল্ট আচরণটি আপনি চান তা না হলে আপনি এখনও একটি কম্বাইনার ফাংশন লিখতে পারেন।)

উদাহরণ: অ্যাডিন্ট কার্নেলে, কোন কম্বাইনার ফাংশন নেই, তাই অ্যাকিউমুলেটর ফাংশন ব্যবহার করা হবে। এটি সঠিক আচরণ, কারণ আমরা যদি একটি মানের সংগ্রহকে দুটি ভাগে ভাগ করি, এবং আমরা সেই দুটি অংশের মানগুলিকে আলাদাভাবে যোগ করি, তাহলে সেই দুটি যোগফল সম্পূর্ণ সংগ্রহকে যোগ করার সমান।

উদাহরণ: findMinAndMax কার্নেলে, কম্বাইনার ফাংশন "সোর্স" অ্যাকুমুলেটর ডেটা আইটেমে রেকর্ড করা ন্যূনতম মান *val "গন্তব্য" সঞ্চয়কারী ডেটা আইটেমে রেকর্ড করা ন্যূনতম মানের চেয়ে কম কিনা তা পরীক্ষা করে *accum , এবং আপডেট *accum সেই অনুযায়ী এটি সর্বোচ্চ মানের জন্য অনুরূপ কাজ করে। এই আপডেটগুলি *accum এ যে অবস্থায় থাকত, যদি সমস্ত ইনপুট মান *accum এ জমা না হয়ে কিছু *accum এ এবং কিছু *val এ জমা হত।

সমস্ত সঞ্চয়কারী ডেটা আইটেম একত্রিত হওয়ার পরে, রেন্ডারস্ক্রিপ্ট জাভাতে ফিরে যাওয়ার জন্য হ্রাসের ফলাফল নির্ধারণ করে। এটি করার জন্য আপনি একটি আউটকনভার্টার ফাংশন লিখতে পারেন। আপনি যদি চান যে সম্মিলিত সঞ্চয়কারী ডেটা আইটেমগুলির চূড়ান্ত মান হ্রাসের ফলাফল হতে চান তবে আপনাকে একটি আউটকনভার্টার ফাংশন লিখতে হবে না।

উদাহরণ: অ্যাডিন্ট কার্নেলে, কোনও আউটকনভার্টার ফাংশন নেই। সম্মিলিত ডেটা আইটেমগুলির চূড়ান্ত মান হল ইনপুটের সমস্ত উপাদানের সমষ্টি, যা আমরা ফেরত দিতে চাই।

উদাহরণ: FindMinAndMax কার্নেলে, আউটকনভারটার ফাংশন একটি int2 ফলাফলের মান শুরু করে যাতে ন্যূনতম এবং সর্বাধিক মানগুলির অবস্থান ধরে রাখা যায় যা সমস্ত সঞ্চয়কারী ডেটা আইটেমগুলির সংমিশ্রণ থেকে তৈরি হয়।

একটি হ্রাস কার্নেল লেখা

#pragma rs reduce একটি রিডাকশন কার্নেলের নাম এবং কার্নেল তৈরিকারী ফাংশনের নাম ও ভূমিকা উল্লেখ করে সংজ্ঞায়িত করে। এই ধরনের সমস্ত ফাংশন static হতে হবে। একটি হ্রাস কার্নেলের সর্বদা একটি accumulator ফাংশন প্রয়োজন; আপনি কার্নেল কি করতে চান তার উপর নির্ভর করে আপনি কিছু বা অন্যান্য ফাংশন বাদ দিতে পারেন।

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

#pragma আইটেমগুলির অর্থ নিম্নরূপ:

  • reduce( kernelName ) (বাধ্যতামূলক): নির্দিষ্ট করে যে একটি হ্রাস কার্নেল সংজ্ঞায়িত করা হচ্ছে। একটি প্রতিফলিত জাভা পদ্ধতি reduce_ kernelName কার্নেল চালু করবে।
  • initializer( initializerName ) (ঐচ্ছিক): এই রিডাকশন কার্নেলের জন্য ইনিশিয়ালাইজার ফাংশনের নাম উল্লেখ করে। আপনি যখন কার্নেল চালু করেন, রেন্ডারস্ক্রিপ্ট প্রতিটি সঞ্চয়কারী ডেটা আইটেমের জন্য একবার এই ফাংশনটিকে কল করে। ফাংশন এই মত সংজ্ঞায়িত করা আবশ্যক:

    static void initializerName(accumType *accum) {  }

    accum এই ফাংশন আরম্ভ করার জন্য একটি সঞ্চয়কারী ডেটা আইটেমের একটি পয়েন্টার।

    আপনি যদি একটি ইনিশিয়ালাইজার ফাংশন প্রদান না করেন, তাহলে রেন্ডারস্ক্রিপ্ট প্রতিটি সঞ্চয়কারী ডেটা আইটেমকে শূন্যে আরম্ভ করে (যেমন memset দ্বারা), এমন আচরণ করে যেন একটি ইনিশিয়ালাইজার ফাংশন আছে যা দেখতে এইরকম:

    static void initializerName(accumType *accum) {
      memset(accum, 0, sizeof(*accum));
    }
  • accumulator( accumulatorName ) (বাধ্যতামূলক): এই রিডাকশন কার্নেলের জন্য সঞ্চয়কারী ফাংশনের নাম উল্লেখ করে। যখন আপনি কার্নেল চালু করেন, তখন ইনপুট(গুলি) অনুযায়ী কোনোভাবে কোনো একুমুলেটর ডেটা আইটেম আপডেট করতে RenderScript ইনপুট(গুলি) এর প্রতিটি স্থানাঙ্কের জন্য একবার এই ফাংশনটিকে কল করে। ফাংশন এই মত সংজ্ঞায়িত করা আবশ্যক:

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

    accum এই ফাংশন পরিবর্তন করার জন্য একটি সঞ্চয়কারী ডেটা আইটেমের একটি পয়েন্টার। in1 থেকে in N হল এক বা একাধিক আর্গুমেন্ট যা কার্নেল লঞ্চে পাস করা ইনপুটের উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে পূর্ণ হয়, প্রতি ইনপুট একটি আর্গুমেন্ট। সঞ্চয়কারী ফাংশন ঐচ্ছিকভাবে যে কোনো বিশেষ আর্গুমেন্ট নিতে পারে।

    একাধিক ইনপুট সহ একটি উদাহরণ কার্নেল হল dotProduct

  • combiner( combinerName )

    (ঐচ্ছিক): এই রিডাকশন কার্নেলের জন্য কম্বাইনার ফাংশনের নাম উল্লেখ করে। RenderScript ইনপুট(গুলি) মধ্যে প্রতিটি স্থানাঙ্কের জন্য একবার সঞ্চয়কারী ফাংশনকে কল করার পরে, এটি সমস্ত সঞ্চয়কারী ডেটা আইটেমকে একটি একক সঞ্চয়কারী ডেটা আইটেমে একত্রিত করার জন্য যতবার প্রয়োজন ততবার এই ফাংশনটিকে কল করে। ফাংশন এই মত সংজ্ঞায়িত করা আবশ্যক:

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

    accum হল এই ফাংশনটি পরিবর্তন করার জন্য একটি "গন্তব্য" সঞ্চয়কারী ডেটা আইটেমের একটি পয়েন্টার। other একটি "উৎস" সঞ্চয়কারী ডেটা আইটেমের একটি নির্দেশক এই ফাংশনের জন্য *accum এ "একত্রিত" করতে।

    দ্রষ্টব্য: এটা সম্ভব যে *accum , *other , বা উভয়ই আরম্ভ করা হয়েছে কিন্তু কখনোই accumulator ফাংশনে পাস করা হয়নি; অর্থাৎ, একটি বা উভয়ই কোনো ইনপুট ডেটা অনুযায়ী আপডেট করা হয়নি। উদাহরণস্বরূপ, findMinAndMax কার্নেলে, কম্বাইনার ফাংশন fMMCombiner স্পষ্টভাবে idx < 0 পরীক্ষা করে কারণ এটি এমন একটি সঞ্চয়কারী ডেটা আইটেম নির্দেশ করে, যার মান হল INITVAL

    আপনি যদি একটি কম্বাইনার ফাংশন প্রদান না করেন, তাহলে রেন্ডারস্ক্রিপ্ট তার জায়গায় অ্যাকিউমুলেটর ফাংশন ব্যবহার করে, এমন আচরণ করে যেন একটি কম্বাইনার ফাংশন আছে যা দেখতে এইরকম:

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

    যদি কার্নেলের একাধিক ইনপুট থাকে, যদি ইনপুট ডেটা টাইপ অ্যাকিউমুলেটর ডেটা টাইপের মতো না হয়, অথবা যদি সঞ্চয়কারী ফাংশন এক বা একাধিক বিশেষ আর্গুমেন্ট নেয় তাহলে একটি কম্বিনার ফাংশন বাধ্যতামূলক।

  • outconverter( outconverterName ) (ঐচ্ছিক): এই রিডাকশন কার্নেলের জন্য outconverter ফাংশনের নাম উল্লেখ করে। রেন্ডারস্ক্রিপ্ট সমস্ত সঞ্চয়কারী ডেটা আইটেম একত্রিত করার পরে, এটি জাভাতে ফিরে যাওয়ার হ্রাসের ফলাফল নির্ধারণ করতে এই ফাংশনটিকে কল করে। ফাংশন এই মত সংজ্ঞায়িত করা আবশ্যক:

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

    result হল একটি ফলাফল ডেটা আইটেমের একটি নির্দেশক (বরাদ্দ করা হয়েছে কিন্তু রেন্ডারস্ক্রিপ্ট রানটাইম দ্বারা আরম্ভ করা হয়নি) এই ফাংশনটি হ্রাসের ফলাফলের সাথে শুরু করার জন্য। resultType হল সেই ডেটা আইটেমের ধরন, যা accumType- এর মতো হতে হবে না। accum হল কম্বাইনার ফাংশন দ্বারা গণনা করা চূড়ান্ত সঞ্চয়কারী ডেটা আইটেমের একটি পয়েন্টার।

    আপনি যদি একটি আউটকনভার্টার ফাংশন প্রদান না করেন, তাহলে রেন্ডারস্ক্রিপ্ট চূড়ান্ত সঞ্চয়কারী ডেটা আইটেমটিকে ফলাফলের ডেটা আইটেমে অনুলিপি করে, এমন আচরণ করে যেন একটি আউটকনভার্টার ফাংশন ছিল যা দেখতে এইরকম:

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

    আপনি যদি সঞ্চয়কারী ডেটা টাইপের চেয়ে ভিন্ন ফলাফলের ধরন চান, তাহলে আউটকনভার্টার ফাংশন বাধ্যতামূলক।

মনে রাখবেন যে একটি কার্নেলের ইনপুট প্রকার, একটি সঞ্চয়কারী ডেটা আইটেম টাইপ এবং একটি ফলাফলের ধরন রয়েছে, যার কোনোটিরই একই রকম হওয়ার দরকার নেই। উদাহরণস্বরূপ, findMinAndMax কার্নেলে, ইনপুট টাইপ long , অ্যাকিউমুলেটর ডেটা আইটেম টাইপ MinAndMax এবং ফলাফলের ধরন int2 সবই আলাদা।

আপনি কি অনুমান করতে পারেন না?

একটি প্রদত্ত কার্নেল লঞ্চের জন্য রেন্ডারস্ক্রিপ্ট দ্বারা তৈরি সঞ্চয়কারী ডেটা আইটেমগুলির সংখ্যার উপর আপনাকে নির্ভর করতে হবে না। কোন গ্যারান্টি নেই যে একই ইনপুট(গুলি) সহ একই কার্নেলের দুটি লঞ্চ একই সংখ্যক সঞ্চয়কারী ডেটা আইটেম তৈরি করবে।

রেন্ডারস্ক্রিপ্ট ইনিশিয়ালাইজার, অ্যাকুমুলেটর এবং কম্বাইনার ফাংশনগুলিকে যে ক্রম অনুসারে কল করে তার উপর আপনাকে নির্ভর করতে হবে না; এটি সমান্তরালভাবে তাদের কিছু কল করতে পারে। কোন গ্যারান্টি নেই যে একই ইনপুট সহ একই কার্নেলের দুটি লঞ্চ একই আদেশ অনুসরণ করবে। একমাত্র গ্যারান্টি হল যে শুধুমাত্র ইনিশিয়ালাইজার ফাংশনটি কখনও একটি অপ্রচলিত সঞ্চয়কারী ডেটা আইটেম দেখতে পাবে। যেমন:

  • কোনো গ্যারান্টি নেই যে সমস্ত সঞ্চয়কারী ডেটা আইটেমগুলি সঞ্চয়কারী ফাংশনটি কল করার আগে শুরু হবে, যদিও এটি শুধুমাত্র একটি প্রারম্ভিক সঞ্চয়কারী ডেটা আইটেমে কল করা হবে।
  • ইনপুট উপাদানগুলি সঞ্চয়কারী ফাংশনে প্রেরণ করা হয় তার কোন গ্যারান্টি নেই।
  • কম্বাইনার ফাংশন কল করার আগে সমস্ত ইনপুট উপাদানগুলির জন্য সঞ্চয়কারী ফাংশন কল করা হয়েছে এমন কোনও গ্যারান্টি নেই৷

এর একটি ফলাফল হল findMinAndMax কার্নেল নির্ধারক নয়: যদি ইনপুটে একই ন্যূনতম বা সর্বোচ্চ মানের একাধিক ঘটনা থাকে, তাহলে কার্নেল কোন ঘটনাটি খুঁজে পাবে তা আপনার জানার কোন উপায় নেই।

আপনি কি নিশ্চয়তা দিতে হবে?

যেহেতু RenderScript সিস্টেম বিভিন্ন উপায়ে একটি কার্নেল চালানোর জন্য বেছে নিতে পারে, আপনার কার্নেল আপনার পছন্দ মতো আচরণ করে তা নিশ্চিত করতে আপনাকে অবশ্যই কিছু নিয়ম অনুসরণ করতে হবে। আপনি যদি এই নিয়মগুলি অনুসরণ না করেন তবে আপনি ভুল ফলাফল, অনির্ধারিত আচরণ বা রানটাইম ত্রুটি পেতে পারেন।

নীচের নিয়মগুলি প্রায়শই বলে যে দুটি সঞ্চয়কারী ডেটা আইটেমের অবশ্যই " একই মান" থাকতে হবে। এর মানে কি? আপনি কার্নেল কি করতে চান তার উপর এটি নির্ভর করে। একটি গাণিতিক হ্রাসের জন্য যেমন addint , এটি সাধারণত গাণিতিক সমতা বোঝাতে "একই" এর অর্থ করে। FindMinAndMax ("সর্বাধিক এবং সর্বনিম্ন ইনপুট মানগুলির অবস্থান খুঁজুন") এর মতো "যেকোনো একটি বাছাই করুন" অনুসন্ধানের জন্য যেখানে অভিন্ন ইনপুট মানগুলির একাধিক ঘটনা থাকতে পারে, একটি প্রদত্ত ইনপুট মানের সমস্ত অবস্থানকে অবশ্যই "একই" হিসাবে বিবেচনা করতে হবে। . আপনি " বাঁদিকের সর্বনিম্ন এবং সর্বোচ্চ ইনপুট মানগুলির অবস্থান খুঁজে পেতে" একটি অনুরূপ কার্নেল লিখতে পারেন যেখানে (বলুন) অবস্থান 100-এ একটি সর্বনিম্ন মান 200-এ অভিন্ন সর্বনিম্ন মানের চেয়ে পছন্দ করা হয়; এই কার্নেলের জন্য, "একই" এর অর্থ হবে অভিন্ন অবস্থান , শুধুমাত্র অভিন্ন মান নয়, এবং সঞ্চয়কারী এবং কম্বাইনার ফাংশন findMinAndMax এর থেকে আলাদা হতে হবে।

ইনিশিয়ালাইজার ফাংশন অবশ্যই একটি আইডেন্টিটি ভ্যালু তৈরি করবে। অর্থাৎ, যদি I এবং A হয় ইনিশিয়ালাইজার ফাংশন দ্বারা সূচনাকৃত একুমুলেটর ডেটা আইটেম, এবং I কখনোই অ্যাকিউমুলেটর ফাংশনে পাস করা হয়নি (তবে A হতে পারে), তাহলে
  • combinerName (& A , & I ) অবশ্যই A একইভাবে ছেড়ে যাবে
  • combinerName (& I , & A ) অবশ্যই I A মতই রেখে যেতে হবে

উদাহরণ: অ্যাডিন্ট কার্নেলে, একটি সঞ্চয়কারী ডেটা আইটেম শূন্য থেকে শুরু হয়। এই কার্নেলের জন্য কম্বাইনার ফাংশন যোগ করে; শূন্য যোগ করার জন্য পরিচয় মান।

উদাহরণ: FindMinAndMax কার্নেলে, একটি সঞ্চয়কারী ডেটা আইটেমকে 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 হয় ইনিশিয়ালাইজার ফাংশন দ্বারা আরম্ভ করা সঞ্চয়কারী ডেটা আইটেম, এবং এটি সঞ্চয়কারী ফাংশনে শূন্য বা তার বেশি বার পাস করা হতে পারে, তাহলে নিম্নলিখিত দুটি কোড ক্রমগুলিকে অবশ্যই A সেট করতে হবে:

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

উদাহরণ: অ্যাডিন্ট কার্নেলে, কম্বাইনার ফাংশন দুটি সঞ্চয়কারী ডেটা আইটেমের মান যোগ করে:

  • 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 আরম্ভ করা হয়নি, এবং args হল ইনপুট আর্গুমেন্টের তালিকা এবং বিশেষ অ্যাকিউমুলেটর ফাংশনে একটি নির্দিষ্ট কলের জন্য আর্গুমেন্ট, তারপর নিম্নলিখিত দুটি কোড সিকোয়েন্সগুলিকে অবশ্যই একই মানতে 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 কার্নেলে, স্থানাঙ্ক X এ একটি ইনপুট মান V এর জন্য:

  • স্টেটমেন্ট 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 সেট করে একই মান, এবং তাই এই কার্নেল মৌলিক ভাঁজ নিয়ম মেনে চলে।

জাভা কোড থেকে একটি হ্রাস কার্নেল কল করা হচ্ছে

kernelName নামের একটি রিডাকশন কার্নেলের জন্য ফাইল filename .rs এ সংজ্ঞায়িত করা হয়েছে, ScriptC_ filename তিনটি পদ্ধতি প্রতিফলিত হয়েছে:

কোটলিন

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

এখানে অ্যাডিন্ট কার্নেল কল করার কিছু উদাহরণ রয়েছে:

কোটলিন

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

পদ্ধতি 1- এ কার্নেলের অ্যাকুমুলেটর ফাংশনে প্রতিটি ইনপুট আর্গুমেন্টের জন্য একটি ইনপুট Allocation আর্গুমেন্ট রয়েছে। RenderScript রানটাইম চেক করে যে সমস্ত ইনপুট বরাদ্দের একই মাত্রা আছে এবং প্রতিটি ইনপুট বরাদ্দের Element ধরন অ্যাকিউমুলেটর ফাংশনের প্রোটোটাইপের সংশ্লিষ্ট ইনপুট আর্গুমেন্টের সাথে মেলে। যদি এই চেকগুলির মধ্যে কোনটি ব্যর্থ হয়, রেন্ডারস্ক্রিপ্ট একটি ব্যতিক্রম নিক্ষেপ করে। কার্নেল সেই মাত্রার প্রতিটি স্থানাঙ্কের উপর কার্যকর করে।

পদ্ধতি 2 পদ্ধতি 1 এর মতই, তবে পদ্ধতি 2 একটি অতিরিক্ত আর্গুমেন্ট sc নেয় যা স্থানাঙ্কের একটি উপসেটে কার্নেল নির্বাহকে সীমাবদ্ধ করতে ব্যবহার করা যেতে পারে।

পদ্ধতি 3 পদ্ধতি 1 এর মতই, বরাদ্দ ইনপুট নেওয়ার পরিবর্তে এটি জাভা অ্যারে ইনপুট নেয়। এটি একটি সুবিধা যা আপনাকে একটি জাভা অ্যারে থেকে স্পষ্টভাবে একটি বরাদ্দ তৈরি করতে এবং এতে ডেটা অনুলিপি করার জন্য কোড লেখার থেকে বাঁচায়৷ যাইহোক, পদ্ধতি 1 এর পরিবর্তে পদ্ধতি 3 ব্যবহার করা কোডের কার্যকারিতা বাড়ায় না । প্রতিটি ইনপুট অ্যারের জন্য, পদ্ধতি 3 উপযুক্ত Element টাইপ এবং setAutoPadding(boolean) সক্ষম সহ একটি অস্থায়ী 1-মাত্রিক বরাদ্দ তৈরি করে, এবং অ্যালোকে অনুলিপি করে যেন Allocation উপযুক্ত copyFrom() পদ্ধতি দ্বারা। এটি তারপর পদ্ধতি 1 কল করে, সেই অস্থায়ী বরাদ্দগুলি পাস করে।

দ্রষ্টব্য: যদি আপনার অ্যাপ্লিকেশান একই অ্যারের সাথে একাধিক কার্নেল কল করে, বা একই মাত্রা এবং এলিমেন্ট টাইপের বিভিন্ন অ্যারের সাথে, আপনি পদ্ধতি 3 ব্যবহার করার পরিবর্তে স্পষ্টভাবে বরাদ্দকরণ তৈরি, পপুলেট এবং পুনরায় ব্যবহার করে কর্মক্ষমতা উন্নত করতে পারেন।

javaFutureType , প্রতিফলিত হ্রাস পদ্ধতির রিটার্ন টাইপ, ScriptC_ filename ক্লাসের মধ্যে একটি প্রতিফলিত স্ট্যাটিক নেস্টেড ক্লাস। এটি একটি হ্রাস কার্নেল চালানোর ভবিষ্যতের ফলাফল উপস্থাপন করে। রানের প্রকৃত ফলাফল পেতে, সেই ক্লাসের get() পদ্ধতিতে কল করুন, যা javaResultType টাইপের একটি মান প্রদান করে। get() সিঙ্ক্রোনাস

কোটলিন

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

জাভা

public class ScriptC_filename extends ScriptC {
  public static class javaFutureType {
    public javaResultType get() {}
  }
}

javaResultType নির্ধারণ করা হয় outconverter ফাংশনের resultType থেকে। যতক্ষণ না resultType একটি স্বাক্ষরবিহীন প্রকার (স্কেলার, ভেক্টর, বা অ্যারে), javaResultType হল সরাসরি সংশ্লিষ্ট জাভা প্রকার। যদি resultType একটি স্বাক্ষরবিহীন প্রকার হয় এবং একটি বড় জাভা স্বাক্ষরিত টাইপ হয়, তাহলে javaResultType হল সেই বড় জাভা স্বাক্ষরিত প্রকার; অন্যথায়, এটি সরাসরি সংশ্লিষ্ট জাভা টাইপ। যেমন:

  • যদি resultType হয় int , int2 , অথবা int[15] , তাহলে javaResultType হল int , Int2 বা int[]ফলাফলের প্রকারের সমস্ত মান javaResultType দ্বারা উপস্থাপন করা যেতে পারে।
  • যদি resultType হয় uint , uint2 , অথবা uint[15] , তাহলে javaResultType long , Long2 , অথবা long[] হয়। ফলাফলের প্রকারের সমস্ত মান javaResultType দ্বারা উপস্থাপন করা যেতে পারে।
  • যদি ফলাফলের প্রকারটি ulong , ulong2 , বা ulong[15] হয়, তাহলে javaResultType long , Long2 বা long[] হয়। ফলাফলের প্রকারের কিছু মান আছে যা javaResultType দ্বারা উপস্থাপন করা যায় না।

javaFutureType হল ভবিষ্যতের ফলাফলের প্রকার যা outconverter ফাংশনের resultType-এর সাথে সম্পর্কিত।

  • যদি resultType একটি অ্যারের প্রকার না হয়, তাহলে javaFutureType হল result_ resultType
  • যদি resultType মেম্বার টাইপের সদস্যদের সাথে দৈর্ঘ্যের একটি অ্যারে হয়, তাহলে javaFutureType হল resultArray Count _ memberType

যেমন:

কোটলিন

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

javaResultType যদি একটি অবজেক্ট টাইপ হয় (একটি অ্যারে টাইপ সহ), একই ইন্সট্যান্সে javaFutureType .get() এ প্রতিটি কল একই অবজেক্ট ফিরিয়ে দেবে।

যদি javaResultType resultType টাইপের সমস্ত মান উপস্থাপন করতে না পারে এবং একটি রিডাকশন কার্নেল একটি অপ্রতিরোধ্য মান তৈরি করে, তাহলে javaFutureType .get() একটি ব্যতিক্রম নিক্ষেপ করে।

পদ্ধতি 3 এবং devecSiInXType

devecSiInXType হল জাভা টাইপ যা অ্যাকিউমুলেটর ফাংশনের সংশ্লিষ্ট আর্গুমেন্টের inXType-এর সাথে সম্পর্কিত। যদি না inXType একটি স্বাক্ষরবিহীন প্রকার বা একটি ভেক্টর প্রকার না হয়, devecSiInXType হল সরাসরি সংশ্লিষ্ট জাভা প্রকার। যদি inXType একটি স্বাক্ষরবিহীন স্কেলার টাইপ হয়, তাহলে devecSiInXType হল জাভা টাইপ যা একই আকারের সাইনড স্কেলার টাইপের সাথে সরাসরি সম্পর্কিত। যদি inXType একটি স্বাক্ষরিত ভেক্টর টাইপ হয়, তাহলে devecSiInXType হল ভেক্টর কম্পোনেন্ট টাইপের সাথে সরাসরি সংশ্লিষ্ট জাভা টাইপ। যদি inXType একটি স্বাক্ষরবিহীন ভেক্টর টাইপ হয়, তাহলে devecSiInXType হল জাভা টাইপ যা সরাসরি ভেক্টর কম্পোনেন্ট টাইপের সাইজের সাইনড স্কেলার টাইপের সাথে সম্পর্কিত। যেমন:

  • যদি inXType int হয়, তাহলে devecSiInXType হল int
  • যদি inXType int2 হয়, তাহলে devecSiInXType হল int । অ্যারেটি একটি সমতল উপস্থাপনা: বরাদ্দের 2-কম্পোনেন্ট ভেক্টর উপাদানগুলির তুলনায় এটিতে দ্বিগুণ স্কেলার উপাদান রয়েছে। এটি একইভাবে যেভাবে copyFrom() Allocation পদ্ধতি কাজ করে।
  • যদি inXType uint হয়, তাহলে deviceSiInXType হল int । জাভা অ্যারেতে একটি স্বাক্ষরিত মানকে বরাদ্দকরণে একই বিটপ্যাটার্নের একটি স্বাক্ষরবিহীন মান হিসাবে ব্যাখ্যা করা হয়। এটি একইভাবে যেভাবে copyFrom() Allocation পদ্ধতি কাজ করে।
  • যদি inXType হয় uint2 , তাহলে deviceSiInXType হল int । এটি int2 এবং uint যেভাবে পরিচালনা করা হয় তার একটি সংমিশ্রণ: অ্যারেটি একটি সমতল উপস্থাপনা, এবং জাভা অ্যারে স্বাক্ষরিত মানগুলিকে রেন্ডারস্ক্রিপ্ট স্বাক্ষরবিহীন এলিমেন্ট মান হিসাবে ব্যাখ্যা করা হয়।

মনে রাখবেন যে পদ্ধতি 3 এর জন্য, ইনপুট প্রকারগুলি ফলাফলের প্রকারের চেয়ে ভিন্নভাবে পরিচালনা করা হয়:

  • একটি স্ক্রিপ্টের ভেক্টর ইনপুট জাভা পাশে সমতল হয়, যেখানে একটি স্ক্রিপ্টের ভেক্টর ফলাফল হয় না।
  • একটি স্ক্রিপ্টের স্বাক্ষরবিহীন ইনপুট জাভা পাশে একই আকারের একটি স্বাক্ষরিত ইনপুট হিসাবে উপস্থাপিত হয়, যেখানে একটি স্ক্রিপ্টের স্বাক্ষরবিহীন ফলাফল জাভা পাশে একটি প্রশস্ত স্বাক্ষরিত প্রকার হিসাবে উপস্থাপিত হয় ( 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গুলির ব্যবহারকে আরও প্রদর্শন করে৷