कम सटीक जानकारी के साथ ऑप्टिमाइज़ करें

ग्राफ़िक्स डेटा और शेडर कैलकुलेशन के अंकों के फ़ॉर्मैट का, आपके गेम की परफ़ॉर्मेंस पर काफ़ी असर पड़ सकता है.

सबसे सही फ़ॉर्मैट के इस्तेमाल के लिए ये बातें ज़रूरी हैं:

  • जीपीयू कैश मेमोरी के इस्तेमाल को ज़्यादा असरदार बनाना
  • मेमोरी बैंडविड्थ की खपत कम करें, पावर बचाएं, और परफ़ॉर्मेंस बढ़ाएं
  • शेडर प्रोग्राम में कंप्यूटेशनल थ्रूपुट को बढ़ाना
  • अपने गेम के लिए सीपीयू रैम का इस्तेमाल कम करना

फ़्लोटिंग पॉइंट फ़ॉर्मैट

आधुनिक 3D ग्राफ़िक्स में ज़्यादातर गणनाओं और डेटा के लिए, फ़्लोटिंग पॉइंट वाली संख्याओं का इस्तेमाल किया जाता है. Android पर Vulkan, 32 या 16 बिट वाले फ़्लोटिंग पॉइंट नंबर का इस्तेमाल करता है. 32-बिट फ़्लोटिंग पॉइंट नंबर को आम तौर पर सिंगल प्रिसिज़न या पूरी सटीक जानकारी कहा जाता है. 16-बिट का फ़्लोटिंग पॉइंट नंबर, आधा सटीक होता है.

Vulkan, 64-बिट फ़्लोटिंग पॉइंट टाइप तय करता है. हालांकि, आम तौर पर Android पर Vulkan डिवाइसों पर यह टाइप काम नहीं करता. साथ ही, इसका इस्तेमाल करने का सुझाव नहीं दिया जाता. 64-बिट वाला फ़्लोटिंग पॉइंट नंबर, आम तौर पर डबल प्रिसिज़न कहा जाता है.

पूर्णांक फ़ॉर्मैट

डेटा और कैलकुलेशन के लिए, हस्ताक्षर वाली और बिना हस्ताक्षर वाली पूर्णांक संख्याओं का भी इस्तेमाल किया जाता है. स्टैंडर्ड इंटिजर का साइज़ 32 बिट होता है. अन्य बिट साइज़ के लिए सहायता डिवाइस पर निर्भर होती है. आम तौर पर, Android पर काम करने वाले Vulkan डिवाइसों पर 16-बिट और 8-बिट वाले पूर्णांक काम करते हैं. Vulkan, 64-बिट इंटिजर टाइप तय करता है. हालांकि, आम तौर पर Android पर Vulkan डिवाइसों पर यह टाइप काम नहीं करता. साथ ही, इसका इस्तेमाल करने का सुझाव नहीं दिया जाता.

आधी सटीक वैल्यू का इस्तेमाल करने पर, बेहतर परफ़ॉर्मेंस नहीं मिलना

आधुनिक जीपीयू आर्किटेक्चर में, 32-बिट पेयर में दो 16-बिट वैल्यू को एक साथ जोड़ा जाता है. साथ ही, पेयर पर काम करने वाले निर्देशों को लागू किया जाता है. बेहतर परफ़ॉर्मेंस के लिए, स्केलर 16-बिट फ़्लोट वैरिएबल का इस्तेमाल करने से बचें. डेटा को दो या फ़ोर एलिमेंट वाले वेक्टर में वेक्टराइज़ करें. शेडर कंपाइलर, वेक्टर ऑपरेशन में स्केलर वैल्यू का इस्तेमाल कर सकता है. हालांकि, स्केलर को ऑप्टिमाइज़ करने के लिए कंपाइलर पर निर्भर रहने पर, वेक्टराइज़ेशन की पुष्टि करने के लिए कंपाइलर आउटपुट की जांच की जाती है.

32-बिट और 16-बिट&#ndash;प्रिसीज़न फ़्लोटिंग पॉइंट में बदलने और उनसे बदलने पर, कंप्यूटेशनल लागत आती है. अपने कोड में सटीक कन्वर्ज़न को कम करके, ओवरहेड कम करें.

अपने एल्गोरिदम के 16-बिट और 32-बिट वर्शन के बीच परफ़ॉर्मेंस में अंतर का बेंचमार्क. हर बार, आधी सटीक वैल्यू का इस्तेमाल करने से परफ़ॉर्मेंस बेहतर नहीं होती. खास तौर पर, मुश्किल कैलकुलेशन के लिए. वेक्टराइज़ किए गए डेटा पर, फ़्यूज़ किए गए गुणा-जोड़ (एफ़एमए) निर्देशों का ज़्यादा इस्तेमाल करने वाले एल्गोरिदम, आधी सटीक जानकारी के साथ बेहतर परफ़ॉर्मेंस के लिए अच्छे विकल्प हैं.

संख्याओं के फ़ॉर्मैट के लिए सहायता

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

वैकल्पिक अंकों वाले फ़ॉर्मैट के लिए, Vulkan में दो कैटगरी की सहायता होती है: अंकगणित और स्टोरेज. किसी खास फ़ॉर्मैट का इस्तेमाल करने से पहले, पक्का करें कि डिवाइस पर वह फ़ॉर्मैट, दोनों कैटगरी में काम करता हो.

अंकगणित से जुड़ी सहायता

किसी अंकों के फ़ॉर्मैट में, शैडर प्रोग्राम में इस्तेमाल किए जा सकने के लिए, Vulkan डिवाइस को अंकगणित की सुविधा का एलान करना होगा. Android पर Vulkan डिवाइसों पर, आम तौर पर अंकगणित के लिए ये फ़ॉर्मैट काम करते हैं:

  • 32-बिट इंटिजर (ज़रूरी है)
  • 32-बिट फ़्लोटिंग पॉइंट (ज़रूरी)
  • 8-बिट इंटिजर (ज़रूरी नहीं)
  • 16-बिट इंटिजर (ज़रूरी नहीं)
  • 16-बिट आधा सटीक फ़्लोटिंग पॉइंट (ज़रूरी नहीं)

यह तय करने के लिए कि क्या Vulkan डिवाइस, अंकगणित के लिए 16-बिट वाले इंटीजर की सुविधा देता है या नहीं, vkGet ऑनलाइनDeviceFeatures2() फ़ंक्शन को कॉल करके डिवाइस की सुविधाएं पाएं. साथ ही, यह भी देखें कि VkMaterialDeviceFeatures2 नतीजे के स्ट्रक्चर में मौजूद 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 है, तो यह फ़ॉर्मैट शेडर प्रोग्राम अंकगणित के साथ काम करता है.

Vulkan 1.1 या 2022 की Android बेसलाइन प्रोफ़ाइल में, VK_KHR_shader_float16_int8 एक्सटेंशन की ज़रूरत नहीं होती. हालांकि, Android डिवाइसों पर VK_KHR_shader_float16_int8 एक्सटेंशन का इस्तेमाल बहुत आम है.

स्टोरेज से जुड़ी सहायता

Vulkan डिवाइस को खास तरह के स्टोरेज के लिए, यह एलान करना होगा कि वह नंबर वाले फ़ॉर्मैट के साथ काम करता है. हालांकि, यह ज़रूरी नहीं है. VK_KHR_16bit_storage एक्सटेंशन, 16-बिट के पूर्णांक और 16-बिट के फ़्लोटिंग-पॉइंट फ़ॉर्मैट के साथ काम करने की सुविधा देता है. एक्सटेंशन के हिसाब से, स्टोरेज के चार टाइप तय किए जाते हैं. किसी डिवाइस पर, 16-बिट की संख्याओं का इस्तेमाल, स्टोरेज के किसी भी टाइप के लिए, कुछ टाइप के लिए या सभी टाइप के लिए किया जा सकता है.

स्टोरेज के टाइप ये हैं:

  • स्टोरेज बफ़र ऑब्जेक्ट
  • यूनिफ़ॉर्म बफ़र ऑब्जेक्ट
  • पुश कॉन्सटेंट ब्लॉक
  • शेडर इनपुट और आउटपुट इंटरफ़ेस

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

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

एक उदाहरण फ़ंक्शन, जो 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;
  }
}

डेटा के लिए सटीक होने का लेवल

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

ऐसे डेटा टाइप जिन्हें हाफ़-प्रिसिशन फ़्लोटिंग पॉइंट में दिखाया जा सकता है:

  • लोकल स्पेस कोऑर्डिनेट में पोज़िशन डेटा
  • छोटे टेक्सचर के लिए, सीमित यूवी रैपिंग वाले टेक्सचर यूवी. इन्हें -1.0 से 1.0 कोऑर्डिनेट रेंज तक सीमित किया जा सकता है
  • सामान्य, टेंगेंट, और बायटेंगेंट डेटा
  • वर्टिक्स का रंग डेटा
  • कम सटीक डेटा, जो 0.0 के आस-पास है

डेटा के ऐसे टाइप जिन्हें हाफ़-प्रिसिशन फ़्लोट में दिखाने का सुझाव नहीं दिया जाता है उनमें ये शामिल हैं:

  • वैश्विक विश्व के निर्देशांकों में डेटा की स्थिति तय करना
  • ज़्यादा सटीक इस्तेमाल के उदाहरणों के लिए टेक्स्चर यूवी, जैसे कि एटलस शीट में यूज़र इंटरफ़ेस (यूआई) एलिमेंट के कोऑर्डिनेट

शेडर कोड में प्रिसिज़न

OpenGL Shading Language (GLSL) और High-level Shader Language (HLSL) शेडर प्रोग्रामिंग भाषाएं, संख्याओं के लिए सटीक वैल्यू या सटीक वैल्यू के बारे में साफ़ तौर पर बताने की सुविधा देती हैं. शेडर कंपाइलर के लिए सुझाव के तौर पर रिलैक्स्ड सटीक होना चाहिए. सटीक जानकारी देना, तय की गई सटीक जानकारी की ज़रूरी शर्त है. Android पर Vulkan डिवाइस, आम तौर पर 16-बिट फ़ॉर्मैट का इस्तेमाल करते हैं. ऐसा तब होता है, जब सटीक जानकारी के लिए ज़्यादा मेहनत न करनी पड़े. Vulkan API का इस्तेमाल करने वाले अन्य डिवाइसों पर, ज़्यादा सटीक डेटा का इस्तेमाल न किया जा सकता. जैसे, ऐसे डेस्कटॉप कंप्यूटर जिनमें 16-बिट फ़ॉर्मैट के साथ काम करने वाले ग्राफ़िक हार्डवेयर का इस्तेमाल किया जा रहा हो. हालांकि, ऐसे डिवाइसों पर 32-बिट फ़ॉर्मैट का इस्तेमाल किया जा सकता है.

जीएलएसएल में स्टोरेज एक्सटेंशन

स्टोरेज और यूनिफ़ॉर्म बफ़र स्ट्रक्चर में 16-बिट या 8-बिट के अंकों वाले फ़ॉर्मैट के साथ काम करने के लिए, सही 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 में इनका कोई बराबर नहीं है.

जीएलएसएल में आसान और सटीक

सिंगल-प्रिसीज़न फ़्लोट का सुझाव देने के लिए, फ़्लोटिंग पॉइंट टाइप से पहले 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 में सटीक वैल्यू

16-बिट फ़्लोटिंग पॉइंट टाइप का इस्तेमाल करने के लिए, अपने 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, 'कम सटीक' के बजाय कम से कम सटीक शब्द का इस्तेमाल करता है. कम से कम सटीक टाइप वाला कीवर्ड, कम से कम सटीक जानकारी देता है. हालांकि, अगर ज़्यादा सटीक जानकारी वाले टारगेट हार्डवेयर के लिए, कंपाइलर ज़्यादा सटीक विकल्प चुन सकता है. min16float कीवर्ड की मदद से, कम से कम सटीक 16-बिट फ़्लोट तय किया जाता है. कम से कम सटीक, साइन वाले, और साइन नहीं वाले 16-बिट पूर्णांक के लिए, min16int और min16uint कीवर्ड का इस्तेमाल किया जाता है. कम सटीक जानकारी देने के कुछ अन्य उदाहरणों में ये शामिल हैं:

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