কম নির্ভুলতার সাথে অপ্টিমাইজ করুন

গ্রাফিক্স ডেটা এবং শেডার গণনার সংখ্যাসূচক বিন্যাস আপনার গেমের পারফরম্যান্সের উপর উল্লেখযোগ্য প্রভাব ফেলতে পারে।

সর্বোত্তম ফর্ম্যাটগুলি নিম্নলিখিত কাজগুলি করে:

  • GPU ক্যাশে ব্যবহারের দক্ষতা বৃদ্ধি করুন
  • মেমোরি ব্যান্ডউইথ খরচ কমানো, শক্তি সঞ্চয় করা এবং কর্মক্ষমতা বৃদ্ধি করা
  • শেডার প্রোগ্রামগুলিতে কম্পিউটেশনাল থ্রুপুট সর্বাধিক করুন
  • আপনার গেমের CPU RAM ব্যবহার কমিয়ে আনুন

ভাসমান বিন্দু বিন্যাস

আধুনিক 3D গ্রাফিক্সের বেশিরভাগ গণনা এবং ডেটা ফ্লোটিং পয়েন্ট নম্বর ব্যবহার করে। অ্যান্ড্রয়েডে Vulkan 32 বা 16 বিট আকারের ফ্লোটিং পয়েন্ট নম্বর ব্যবহার করে। একটি 32-বিট ফ্লোটিং পয়েন্ট নম্বরকে সাধারণত একক নির্ভুলতা বা পূর্ণ নির্ভুলতা বলা হয়; একটি 16-বিট ফ্লোটিং পয়েন্ট নম্বর, অর্ধ নির্ভুলতা।

ভলকান একটি 64-বিট ফ্লোটিং পয়েন্ট টাইপ সংজ্ঞায়িত করে, কিন্তু এই টাইপটি সাধারণত অ্যান্ড্রয়েডে ভলকান ডিভাইসগুলিতে সমর্থিত নয় এবং এর ব্যবহার সুপারিশ করা হয় না। 64-বিট ফ্লোটিং পয়েন্ট নম্বরকে সাধারণত ডাবল প্রিসিশন বলা হয়।

পূর্ণসংখ্যার বিন্যাস

স্বাক্ষরিত এবং স্বাক্ষরবিহীন পূর্ণসংখ্যা সংখ্যাগুলিও ডেটা এবং গণনার জন্য ব্যবহৃত হয়। স্ট্যান্ডার্ড পূর্ণসংখ্যার আকার 32 বিট। অন্যান্য বিট আকারের জন্য সমর্থন ডিভাইসের উপর নির্ভর করে। অ্যান্ড্রয়েড চালিত ভলকান ডিভাইসগুলি সাধারণত 16-বিট এবং 8-বিট পূর্ণসংখ্যা সমর্থন করে। ভলকান একটি 64-বিট পূর্ণসংখ্যার ধরণ সংজ্ঞায়িত করে, তবে অ্যান্ড্রয়েডে ভলকান ডিভাইসগুলিতে এই ধরণটি সাধারণত সমর্থিত নয় এবং এর ব্যবহার সুপারিশ করা হয় না।

সাবঅপ্টিমাল অর্ধ-নির্ভুলতা আচরণ

আধুনিক GPU আর্কিটেকচার দুটি 16-বিট মানকে 32-বিট জোড়ায় একত্রিত করে এবং জোড়ার উপর কাজ করে এমন নির্দেশাবলী বাস্তবায়ন করে। সর্বোত্তম কর্মক্ষমতার জন্য, স্কেলার 16-বিট ফ্লোট ভেরিয়েবল ব্যবহার করা এড়িয়ে চলুন; ডেটাকে দুই বা চার-উপাদান ভেক্টরে ভেক্টরাইজ করুন। শেডার কম্পাইলার ভেক্টর অপারেশনে স্কেলার মান ব্যবহার করতে সক্ষম হতে পারে। তবে, যদি আপনি স্কেলার অপ্টিমাইজ করার জন্য কম্পাইলারের উপর নির্ভর করেন, তাহলে ভেক্টরাইজেশন যাচাই করার জন্য কম্পাইলার আউটপুট পরীক্ষা করুন।

৩২-বিট এবং ১৬-বিট-প্রিসিশন ফ্লোটিং পয়েন্টে রূপান্তর করার জন্য একটি গণনামূলক খরচ হয়। আপনার কোডে প্রিসিশন রূপান্তর কমিয়ে ওভারহেড কমান।

১৬-বিট এবং ৩২-বিট সংস্করণের অ্যালগরিদমের মধ্যে পারফরম্যান্সের পার্থক্যের মানদণ্ড নির্ধারণ করুন। অর্ধ-নির্ভুলতা সর্বদা কর্মক্ষমতা উন্নত করে না, বিশেষ করে জটিল গণনার ক্ষেত্রে। ভেক্টরাইজড ডেটাতে ফিউজড মাল্টিপ্লাই-অ্যাড (FMA) নির্দেশাবলীর ব্যাপক ব্যবহার করে এমন অ্যালগরিদমগুলি অর্ধ-নির্ভুলতায় উন্নত কর্মক্ষমতার জন্য ভাল প্রার্থী।

সংখ্যাসূচক বিন্যাস সমর্থন

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

Vulkan-এর ঐচ্ছিক সংখ্যাসূচক ফর্ম্যাটের জন্য দুটি বিভাগের সমর্থন রয়েছে: গাণিতিক এবং সঞ্চয়স্থান। একটি নির্দিষ্ট ফর্ম্যাট ব্যবহার করার আগে, নিশ্চিত করুন যে একটি ডিভাইস উভয় বিভাগেই এটি সমর্থন করে।

পাটিগণিত সহায়তা

একটি ভালকান ডিভাইসকে শেডার প্রোগ্রামগুলিতে ব্যবহারযোগ্য করার জন্য একটি সংখ্যাসূচক বিন্যাসের জন্য গাণিতিক সমর্থন ঘোষণা করতে হবে। অ্যান্ড্রয়েডে ভালকান ডিভাইসগুলি সাধারণত গাণিতিকের জন্য নিম্নলিখিত ফর্ম্যাটগুলিকে সমর্থন করে:

  • ৩২-বিট পূর্ণসংখ্যা (বাধ্যতামূলক)
  • ৩২-বিট ফ্লোটিং পয়েন্ট (বাধ্যতামূলক)
  • ৮-বিট পূর্ণসংখ্যা (ঐচ্ছিক)
  • ১৬-বিট পূর্ণসংখ্যা (ঐচ্ছিক)
  • ১৬-বিট অর্ধ-নির্ভুল ভাসমান বিন্দু (ঐচ্ছিক)

একটি Vulkan ডিভাইস পাটিগণিতের জন্য 16-বিট পূর্ণসংখ্যা সমর্থন করে কিনা তা নির্ধারণ করতে, vkGetPhysicalDeviceFeatures2() ফাংশনটি কল করে এবং VkPhysicalDeviceFeatures2 ফলাফল কাঠামোর shaderInt16 ক্ষেত্রটি সত্য কিনা তা পরীক্ষা করে ডিভাইসের বৈশিষ্ট্যগুলি পুনরুদ্ধার করুন।

একটি Vulkan ডিভাইস 16-বিট ফ্লোট নাকি 8-বিট পূর্ণসংখ্যা সমর্থন করে তা নির্ধারণ করতে, নিম্নলিখিত পদক্ষেপগুলি সম্পাদন করুন:

  1. ডিভাইসটি VK_KHR_shader_float16_int8 Vulkan এক্সটেনশন সমর্থন করে কিনা তা পরীক্ষা করুন। 16-বিট ফ্লোট এবং 8-বিট ইন্টিজার সাপোর্টের জন্য এক্সটেনশনটি প্রয়োজন।
  2. যদি VK_KHR_shader_float16_int8 সমর্থিত হয়, তাহলে একটি VkPhysicalDeviceFeatures2.pNext চেইনে একটি VkPhysicalDeviceShaderFloat16Int8Features স্ট্রাকচার পয়েন্টার যুক্ত করুন।
  3. vkGetPhysicalDeviceFeatures2() কল করার পর VkPhysicalDeviceShaderFloat16Int8Features ফলাফল কাঠামোর shaderFloat16 এবং shaderInt8 ক্ষেত্রগুলি পরীক্ষা করুন। যদি ক্ষেত্রের মান true হয়, তাহলে shader প্রোগ্রামের গাণিতিকের জন্য ফর্ম্যাটটি সমর্থিত।

যদিও Vulkan 1.1 বা 2022 Android Baseline প্রোফাইলে এটি প্রয়োজনীয় নয়, তবুও Android ডিভাইসগুলিতে VK_KHR_shader_float16_int8 এক্সটেনশনের জন্য সমর্থন খুবই সাধারণ।

স্টোরেজ সাপোর্ট

একটি Vulkan ডিভাইসকে নির্দিষ্ট ধরণের স্টোরেজের জন্য একটি ঐচ্ছিক সংখ্যাসূচক ফর্ম্যাটের জন্য সমর্থন ঘোষণা করতে হবে। VK_KHR_16bit_storage এক্সটেনশনটি 16-বিট পূর্ণসংখ্যা এবং 16-বিট ফ্লোটিং-পয়েন্ট ফর্ম্যাটের জন্য সমর্থন ঘোষণা করে। এক্সটেনশন দ্বারা চারটি স্টোরেজ প্রকার সংজ্ঞায়িত করা হয়েছে। একটি ডিভাইস 16-বিট সংখ্যাকে কোনও, কিছু, অথবা সমস্ত স্টোরেজ প্রকারের জন্য সমর্থন করতে পারে।

স্টোরেজের ধরণগুলি হল:

  • স্টোরেজ বাফার অবজেক্ট
  • ইউনিফর্ম বাফার অবজেক্ট
  • ধ্রুবক ব্লক পুশ করুন
  • শেডার ইনপুট এবং আউটপুট ইন্টারফেস

বেশিরভাগ, কিন্তু সব নয়, অ্যান্ড্রয়েডের Vulkan 1.1 ডিভাইস স্টোরেজ বাফার অবজেক্টে 16-বিট ফর্ম্যাট সমর্থন করে। GPU মডেলের উপর ভিত্তি করে সমর্থন ধরে নেবেন না। প্রদত্ত GPU-এর জন্য পুরানো ড্রাইভার সহ ডিভাইসগুলি স্টোরেজ বাফার অবজেক্ট সমর্থন নাও করতে পারে, যখন নতুন ড্রাইভার সহ ডিভাইসগুলি করে।

ইউনিফর্ম বাফার, পুশ কনস্ট্যান্ট ব্লক এবং শেডার ইনপুট/আউটপুট ইন্টারফেসে ১৬-বিট ফরম্যাটের জন্য সমর্থন সাধারণত জিপিইউ প্রস্তুতকারকের উপর নির্ভর করে। অ্যান্ড্রয়েডে, একটি জিপিইউ সাধারণত এই তিনটি ধরণেরই সমর্থন করে, অথবা কোনওটিই সমর্থন করে না।

Vulkan গাণিতিক এবং স্টোরেজ ফর্ম্যাট সমর্থন পরীক্ষা করার জন্য একটি উদাহরণ ফাংশন:

struct ReducedPrecisionSupportInfo {
  // Arithmetic support
  bool has_8_bit_int_ = false;
  bool has_16_bit_int_ = false;
  bool has_16_bit_float_ = false;
  // Storage support
  bool has_16_bit_SSBO_ = false;
  bool has_16_bit_UBO_ = false;
  bool has_16_bit_push_ = false;
  bool has_16_bit_input_output_ = false;
  // Use 16-bit floats if we have arithmetic
  // support and at least SSBO storage support.
  bool use_16bit_floats_ = false;
};

void CheckFormatSupport(VkPhysicalDevice physical_device,
    ReducedPrecisionSupportInfo &info) {

  // Retrieve the device extension list so we
  // can check for our desired extensions.
  uint32_t device_extension_count;
  vkEnumerateDeviceExtensionProperties(physical_device, nullptr,
      &device_extension_count, nullptr);
  std::vector<VkExtensionProperties> device_extensions(device_extension_count);
  vkEnumerateDeviceExtensionProperties(physical_device, nullptr,
      &device_extension_count, device_extensions.data());

  bool has_16_8_extension = HasDeviceExtension("VK_KHR_shader_float16_int8",
      device_extensions);

  // Initialize the device features structure and
  // chain the storage features structure and 8/16-bit
  // support structure if applicable.
  VkPhysicalDeviceFeatures2 device_features;
  memset(&device_features, 0, sizeof(device_features));
  device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;

  VkPhysicalDeviceShaderFloat16Int8Features f16_int8_features;
  memset(&f16_int8_features, 0, sizeof(f16_int8_features));
  f16_int8_features.sType =
      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR;

  VkPhysicalDevice16BitStorageFeatures storage_features;
  memset(&storage_features, 0, sizeof(storage_features));
  storage_features.sType =
      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
  device_features.pNext = &storage_features;

  if (has_16_8_extension) {
    storage_features.pNext = &f16_int8_features;
  }

  vkGetPhysicalDeviceFeatures2(physical_device, &device_features);

  // Parse the storage features and determine
  // what kinds of 16-bit storage access are available.
  if (storage_features.storageBuffer16BitAccess ||
      storage_features.uniformAndStorageBuffer16BitAccess) {
    info.has_16_bit_SSBO_ = true;
  }
  info.has_16_bit_UBO_ = storage_features.uniformAndStorageBuffer16BitAccess;
  info.has_16_bit_push_ = storage_features.storagePushConstant16;
  info.has_16_bit_input_output_ = storage_features.storageInputOutput16;

  info.has_16_bit_int_ = device_features.features.shaderInt16;
  if (has_16_8_extension) {
    info.has_16_bit_float_ = f16_int8_features.shaderFloat16;
    info.has_8_bit_int_ = f16_int8_features.shaderInt8;
  }

  // Get arithmetic and at least some form of storage
  // support before enabling 16-bit float usage.
  if (info.has_16_bit_float_ && info.has_16_bit_SSBO_) {
    info.use_16bit_floats_ = true;
  }
}

ডেটার জন্য যথার্থ স্তর

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

অর্ধ-নির্ভুল ভাসমান বিন্দুতে উপস্থাপনের জন্য ভালো প্রার্থী ডেটা টাইপগুলির মধ্যে রয়েছে:

  • স্থানীয় স্থান স্থানাঙ্কে অবস্থান তথ্য
  • সীমিত UV মোড়ক সহ ছোট টেক্সচারের জন্য টেক্সচার UV যা -1.0 থেকে 1.0 স্থানাঙ্ক পরিসরে সীমাবদ্ধ থাকতে পারে
  • সাধারণ, ট্যানজেন্ট এবং বিট্যাঞ্জেন্ট ডেটা
  • ভার্টেক্স রঙের ডেটা
  • ০.০ কেন্দ্রিক কম নির্ভুলতার প্রয়োজনীয়তা সহ ডেটা

অর্ধ-নির্ভুল ফ্লোটে উপস্থাপনের জন্য সুপারিশ করা হয় না এমন ডেটা প্রকারগুলির মধ্যে রয়েছে:

  • বিশ্বব্যাপী বিশ্ব স্থানাঙ্কে অবস্থানের তথ্য
  • অ্যাটলাস শিটে UI উপাদান স্থানাঙ্কের মতো উচ্চ-নির্ভুল ব্যবহারের ক্ষেত্রে টেক্সচার UV

শেডার কোডে যথার্থতা

OpenGL শেডিং ল্যাঙ্গুয়েজ (GLSL) এবং হাই-লেভেল শেডার ল্যাঙ্গুয়েজ (HLSL) শেডার প্রোগ্রামিং ল্যাঙ্গুয়েজগুলি সংখ্যাসূচক ধরণের জন্য রিল্যাক্সড প্রিসিশন বা স্পষ্ট স্পষ্টতার স্পেসিফিকেশন সমর্থন করে। রিল্যাক্সড প্রিসিশনকে শেডার কম্পাইলারের জন্য সুপারিশ হিসাবে বিবেচনা করা হয়। স্পষ্ট স্পষ্টতা নির্দিষ্ট নির্ভুলতার একটি প্রয়োজনীয়তা। অ্যান্ড্রয়েডে ভলকান ডিভাইসগুলি সাধারণত রিল্যাক্সড প্রিসিশন দ্বারা প্রস্তাবিত হলে 16-বিট ফর্ম্যাট ব্যবহার করে। অন্যান্য ভলকান ডিভাইসগুলি, বিশেষ করে ডেস্কটপ কম্পিউটারগুলিতে গ্রাফিক্স হার্ডওয়্যার ব্যবহার করে যেখানে 16-বিট ফর্ম্যাটের জন্য সমর্থন নেই, রিল্যাক্সড প্রিসিশন উপেক্ষা করতে পারে এবং এখনও 32-বিট ফর্ম্যাট ব্যবহার করতে পারে।

GLSL-এ স্টোরেজ এক্সটেনশন

স্টোরেজ এবং ইউনিফর্ম বাফার স্ট্রাকচারে ১৬-বিট বা ৮-বিট সংখ্যাসূচক ফর্ম্যাটের জন্য সমর্থন সক্ষম করার জন্য উপযুক্ত GLSL এক্সটেনশনগুলি সংজ্ঞায়িত করতে হবে। প্রাসঙ্গিক এক্সটেনশন ঘোষণাগুলি হল:

// Enable 16-bit formats in storage and uniform buffers.
#extension GL_EXT_shader_16bit_storage : require
// Enable 8-bit formats in storage and uniform buffers.
#extension GL_EXT_shader_8bit_storage : require

এই এক্সটেনশনগুলি GLSL-এর জন্য নির্দিষ্ট এবং HLSL-এ এর সমতুল্য কোনও বৈশিষ্ট্য নেই।

GLSL-এ স্বাচ্ছন্দ্যপূর্ণ নির্ভুলতা

একটি ফ্লোটিং পয়েন্ট টাইপের আগে highp কোয়ালিফায়ার ব্যবহার করে একটি সিঙ্গেল-প্রিসিশন ফ্লোট এবং একটি হাফ-প্রিসিশন ফ্লোটের জন্য mediump কোয়ালিফায়ার ব্যবহার করুন। Vulkan-এর GLSL কম্পাইলাররা লিগ্যাসি lowp কোয়ালিফায়ারকে mediump হিসেবে ব্যাখ্যা করে। রিল্যাক্সড প্রিসিশনের কিছু উদাহরণ:

mediump vec4 my_vector; // Suggest 16-bit half precision
highp mat4 my_matrix;   // Suggest 32-bit single precision

GLSL-এ স্পষ্ট নির্ভুলতা

১৬-বিট ফ্লোটিং পয়েন্ট টাইপ ব্যবহার সক্ষম করতে আপনার GLSL কোডে GL_EXT_shader_explicit_arithmetic_types_float16 এক্সটেনশনটি অন্তর্ভুক্ত করুন:

#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require

নিম্নলিখিত কীওয়ার্ড ব্যবহার করে GLSL-এ 16-বিট ফ্লোটিং পয়েন্ট স্কেলার, ভেক্টর এবং ম্যাট্রিক্স প্রকার ঘোষণা করুন:

float16_t   f16vec2     f16vec3    f16vec4
f16mat2     f16mat3     f16mat4
f16mat2x2   f16mat2x3   f16mat2x4
f16mat3x2   f16mat3x3   f16mat3x4
f16mat4x2   f16mat4x3   f16mat4x4

নিম্নলিখিত কীওয়ার্ড ব্যবহার করে GLSL-এ 16-বিট পূর্ণসংখ্যা স্কেলার এবং ভেক্টর প্রকার ঘোষণা করুন:

int16_t     i16vec2     i16vec3    i16vec4
uint16_t    u16vec2     u16vec3    u16vec4

HLSL-এ স্বাচ্ছন্দ্যপূর্ণ নির্ভুলতা

HLSL "relaxed precision" শব্দটি ব্যবহার করে না, বরং " minimal precision" শব্দটি ব্যবহার করে। "minimal precision" টাইপের একটি কীওয়ার্ড "minimal precision" শব্দটি ব্যবহার করে, কিন্তু যদি "higher precision" টার্গেট হার্ডওয়্যারের জন্য ভালো পছন্দ হয়, তাহলে কম্পাইলারটি "higher precision" শব্দটি ব্যবহার করে একটি উচ্চতর precision ব্যবহার করতে পারে। "minimal precision 16-bit float" শব্দটি min16float শব্দ দ্বারা নির্দিষ্ট করা হয়। "minimal precision signed" এবং "unsigned" 16-bit পূর্ণসংখ্যা যথাক্রমে min16int এবং min16uint শব্দ দ্বারা নির্দিষ্ট করা হয়। "minimal precision declarations" এর অতিরিক্ত উদাহরণগুলির মধ্যে রয়েছে:

// Four element vector and four-by-four matrix types
min16float4 my_vector4;
min16float4x4 my_matrix4x4;

HLSL-এ স্পষ্ট নির্ভুলতা

অর্ধ-নির্ভুলতা ভাসমান-পয়েন্ট half বা float16_t কীওয়ার্ড দ্বারা নির্দিষ্ট করা হয়। স্বাক্ষরিত এবং স্বাক্ষরবিহীন 16-বিট পূর্ণসংখ্যা যথাক্রমে int16_t এবং uint16_t কীওয়ার্ড দ্বারা নির্দিষ্ট করা হয়। স্পষ্ট নির্ভুলতা ঘোষণার অতিরিক্ত উদাহরণগুলির মধ্যে রয়েছে:

// Four element vector and four-by-four matrix types
half4 my_vector4;
half4x4 my_matrix4x4;