ग्राफ़िक्स डेटा और शेडर कैलकुलेशन के न्यूमेरिक फ़ॉर्मैट का, आपके गेम की परफ़ॉर्मेंस पर काफ़ी असर पड़ सकता है.
ऑप्टिमल फ़ॉर्मैट से ये फ़ायदे मिलते हैं:
- जीपीयू कैश मेमोरी के इस्तेमाल की क्षमता बढ़ती है
- मेमोरी बैंडविड्थ की खपत कम होती है, जिससे पावर की बचत होती है और परफ़ॉर्मेंस बेहतर होती है
- शेडर प्रोग्राम में कंप्यूटेशनल थ्रूपुट बढ़ता है
- आपके गेम में सीपीयू रैम का इस्तेमाल कम होता है
फ़्लोटिंग पॉइंट फ़ॉर्मैट
मॉडर्न 3D ग्राफ़िक्स में, ज़्यादातर कैलकुलेशन और डेटा के लिए फ़्लोटिंग पॉइंट नंबर का इस्तेमाल किया जाता है. Android पर Vulkan, 32 या 16 बिट साइज़ वाले फ़्लोटिंग पॉइंट नंबर का इस्तेमाल करता है. आम तौर पर, 32-बिट फ़्लोटिंग पॉइंट नंबर को सिंगल प्रीसिज़न या फ़ुल प्रीसिज़न कहा जाता है. वहीं, 16-बिट फ़्लोटिंग पॉइंट नंबर को हाफ़ प्रीसिज़न कहा जाता है.
Vulkan, 64-बिट फ़्लोटिंग पॉइंट टाइप को तय करता है. हालांकि, Android पर Vulkan डिवाइस आम तौर पर इस टाइप के साथ काम नहीं करते. इसलिए, इसका इस्तेमाल करने का सुझाव नहीं दिया जाता. आम तौर पर, 64-बिट फ़्लोटिंग पॉइंट नंबर को डबल प्रीसिज़न कहा जाता है.
इंटिजर फ़ॉर्मैट
डेटा और कैलकुलेशन के लिए, साइंड और अनसाइंड इंटिजर नंबर का भी इस्तेमाल किया जाता है. स्टैंडर्ड इंटिजर साइज़ 32 बिट होता है. अन्य बिट साइज़ के लिए सहायता, डिवाइस पर निर्भर करती है. Android पर Vulkan डिवाइस आम तौर पर 16-बिट और 8-बिट इंटिजर के साथ काम करते हैं. Vulkan, 64-बिट इंटिजर टाइप को तय करता है. हालांकि, Android पर Vulkan डिवाइस आम तौर पर इस टाइप के साथ काम नहीं करते. इसलिए, इसका इस्तेमाल करने का सुझाव नहीं दिया जाता.
हाफ़-प्रिसिज़न के साथ काम न करने की समस्या
मॉडर्न जीपीयू आर्किटेक्चर, 16-बिट की दो वैल्यू को मिलाकर 32-बिट का पेयर बनाता है. साथ ही, ऐसे निर्देश लागू करता है जो इस पेयर पर काम करते हैं. ऑप्टिमल परफ़ॉर्मेंस के लिए, स्केलर 16-बिट फ़्लोट वैरिएबल का इस्तेमाल न करें. डेटा को दो या चार एलिमेंट वाले वेक्टर में बदलें. शेडर कंपाइलर, वेक्टर ऑपरेशन में स्केलर वैल्यू का इस्तेमाल कर सकता है. हालांकि, अगर स्केलर को ऑप्टिमाइज़ करने के लिए कंपाइलर पर निर्भर हैं, तो वेक्टर में बदलने की पुष्टि करने के लिए कंपाइलर के आउटपुट की जांच करें.
32-बिट और 16-बिट प्रीसिज़न फ़्लोटिंग पॉइंट में बदलने और इससे वापस 32-बिट और 16-बिट प्रीसिज़न फ़्लोटिंग पॉइंट में बदलने पर, कंप्यूटेशनल लागत लगती है. अपने कोड में प्रीसिज़न कन्वर्ज़न को कम करके, ओवरहेड कम करें.
अपने एल्गोरिदम के 16-बिट और 32-बिट वर्शन के बीच, बेंचमार्क परफ़ॉर्मेंस के अंतर की तुलना करें. हाफ़ प्रीसिज़न से हमेशा परफ़ॉर्मेंस बेहतर नहीं होती. खास तौर पर, मुश्किल कैलकुलेशन के लिए. वेक्टर में बदले गए डेटा पर, फ़्यूज़्ड मल्टिप्लाई-ऐड (एफ़एमए) निर्देशों का ज़्यादा इस्तेमाल करने वाले एल्गोरिदम, हाफ़ प्रीसिज़न पर बेहतर परफ़ॉर्मेंस के लिए सही विकल्प हैं.
न्यूमेरिक फ़ॉर्मैट के लिए सहायता
Android पर सभी Vulkan डिवाइस, डेटा और शेडर कैलकुलेशन में सिंगल-प्रिसिज़न, 32-बिट फ़्लोटिंग पॉइंट नंबर और 32-बिट इंटिजर नंबर के साथ काम करते हैं. अन्य फ़ॉर्मैट के लिए सहायता उपलब्ध होने की कोई गारंटी नहीं है. अगर सहायता उपलब्ध है, तो भी सभी इस्तेमाल के मामलों में सहायता उपलब्ध होने की कोई गारंटी नहीं है.
Vulkan में, वैकल्पिक न्यूमेरिक फ़ॉर्मैट के लिए दो तरह की सहायता उपलब्ध है: अरिथमेटिक और स्टोरेज. किसी खास फ़ॉर्मैट का इस्तेमाल करने से पहले, पक्का करें कि कोई डिवाइस इन दोनों कैटगरी में उस फ़ॉर्मैट के साथ काम करता हो.
अरिथमेटिक के लिए सहायता
शेडर प्रोग्राम में इस्तेमाल करने के लिए, Vulkan डिवाइस को किसी न्यूमेरिक फ़ॉर्मैट के लिए अरिथमेटिक सहायता की घोषणा करनी होगी. Android पर Vulkan डिवाइस आम तौर पर, अरिथमेटिक के लिए इन फ़ॉर्मैट के साथ काम करते हैं:
- 32-बिट इंटिजर (ज़रूरी)
- 32-बिट फ़्लोटिंग पॉइंट (ज़रूरी)
- 8-बिट इंटिजर (ज़रूरी नहीं)
- 16-बिट इंटिजर (ज़रूरी नहीं)
- 16-बिट हाफ़-प्रिसिज़न फ़्लोटिंग पॉइंट (ज़रूरी नहीं)
यह पता करने के लिए कि कोई Vulkan डिवाइस, अरिथमेटिक के लिए 16-बिट इंटिजर के साथ काम करता है या नहीं,
vkGetPhysicalDeviceFeatures2() फ़ंक्शन को कॉल करके डिवाइस की सुविधाओं को वापस पाएं. इसके बाद, देखें कि VkPhysicalDeviceFeatures2 के नतीजे वाले स्ट्रक्चर में shaderInt16 फ़ील्ड की वैल्यू 'सही' है या नहीं.
यह पता करने के लिए कि कोई Vulkan डिवाइस, 16-बिट फ़्लोट या 8-बिट इंटिजर के साथ काम करता है या नहीं, यह तरीका अपनाएं:
- देखें कि डिवाइस, VK_KHR_shader_float16_int8 Vulkan एक्सटेंशन के साथ काम करता है या नहीं. 16-बिट फ़्लोट और 8-बिट इंटिजर के लिए सहायता पाने के लिए, यह एक्सटेंशन ज़रूरी है.
- अगर
VK_KHR_shader_float16_int8के लिए सहायता उपलब्ध है, तोVkPhysicalDeviceFeatures2.pNextचेन में VkPhysicalDeviceShaderFloat16Int8Features स्ट्रक्चर पॉइंटर जोड़ें. vkGetPhysicalDeviceFeatures2()को कॉल करने के बाद,VkPhysicalDeviceShaderFloat16Int8Featuresके नतीजे वाले स्ट्रक्चर केshaderFloat16औरshaderInt8फ़ील्ड देखें. अगर फ़ील्ड की वैल्यूtrueहै, तो शेडर प्रोग्राम अरिथमेटिक के लिए, इस फ़ॉर्मैट के साथ काम किया जा सकता है.
Vulkan 1.1 या 2022
Android Baseline प्रोफ़ाइल में, VK_KHR_shader_float16_int8
एक्सटेंशन के लिए सहायता ज़रूरी नहीं है. हालांकि, Android डिवाइसों पर आम तौर पर यह सुविधा उपलब्ध होती है.
स्टोरेज के लिए सहायता
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 पर केंद्रित, कम प्रीसिज़न की ज़रूरत वाला डेटा
हाफ़-प्रिसिज़न फ़्लोट में दिखाने के लिए, इन डेटा टाइप का इस्तेमाल करने का सुझाव नहीं दिया जाता:
- ग्लोबल वर्ल्ड कोऑर्डिनेट में पोज़िशन डेटा
- हाई-प्रिसिज़न इस्तेमाल के मामलों के लिए टेक्सचर यूवी. जैसे, एटलस शीट में यूआई एलिमेंट कोऑर्डिनेट
शेडर कोड में प्रीसिज़न
The OpenGL Shading Language (GLSL) और High-level Shader Language (HLSL) शेडर प्रोग्रामिंग भाषाएं, न्यूमेरिक टाइप के लिए रिलैक्स्ड प्रीसिज़न या एक्सप्लिसिट प्रीसिज़न तय करने की सुविधा देती हैं. रिलैक्स्ड प्रीसिज़न को शेडर कंपाइलर के लिए सुझाव के तौर पर माना जाता है. एक्सप्लिसिट प्रीसिज़न, तय किए गए प्रीसिज़न की ज़रूरी शर्त है. Android पर Vulkan डिवाइस आम तौर पर, रिलैक्स्ड प्रीसिज़न के सुझाव के मुताबिक, 16-बिट फ़ॉर्मैट का इस्तेमाल करते हैं. अन्य Vulkan डिवाइस, खास तौर पर डेस्कटॉप कंप्यूटर पर, 16-बिट फ़ॉर्मैट के लिए सहायता न देने वाले ग्राफ़िक्स हार्डवेयर का इस्तेमाल करने वाले डिवाइस, रिलैक्स्ड प्रीसिज़न को अनदेखा कर सकते हैं और 32-बिट फ़ॉर्मैट का इस्तेमाल कर सकते हैं.
GLSL में स्टोरेज एक्सटेंशन
स्टोरेज और यूनिफ़ॉर्म बफ़र स्ट्रक्चर में, 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 में कोई विकल्प नहीं है.
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 में एक्सप्लिसिट प्रीसिज़न
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-बिट फ़्लोट तय किया जाता है. min16int और min16uint कीवर्ड से, क्रमशः मिनिमल प्रीसिज़न साइंड और अनसाइंड 16-बिट इंटिजर तय किए जाते हैं. मिनिमल प्रीसिज़न की घोषणाओं के अन्य उदाहरणों में ये शामिल हैं:
// 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;