يمكن أن يؤثر التنسيق الرقمي لبيانات الرسومات وعمليات احتساب تأثيرات التظليل في أداء لعبتك بشكل كبير.
تحقّق التنسيقات المثلى ما يلي:
- زيادة كفاءة استخدام ذاكرة التخزين المؤقت لوحدة معالجة الرسومات
- تقليل استهلاك معدل نقل البيانات في الذاكرة، وتوفير الطاقة وزيادة الأداء
- زيادة معدل نقل البيانات الحسابية إلى أقصى حد في برامج Shader
- تقليل استخدام ذاكرة الوصول العشوائي (RAM) لوحدة المعالجة المركزية في لعبتك
تنسيقات النقطة العائمة
تستخدم معظم العمليات الحسابية والبيانات في الرسومات الثلاثية الأبعاد الحديثة أرقامًا بنقطة عائمة. يستخدم Vulkan على Android أعدادًا نقطية عائمة بحجم 32 أو 16 بت. يُشار عادةً إلى عدد النقطة العائمة 32 بت باسم دقة واحدة أو دقة كاملة، وعدد النقطة العائمة 16 بت باسم دقة نصفية.
يحدِّد Vulkan نوعًا بنقطة عائمة 64 بت، ولكن لا يتوافق النوع بشكل شائع مع أجهزة Vulkan على Android، ولا يُنصح باستخدامه. يُشار عادةً إلى عدد فاصل عائم بسعة 64 بت باسم الدقة المزدوجة.
تنسيقات الأعداد الصحيحة
تُستخدَم الأعداد الصحيحة الموجبة والسلبية أيضًا للبيانات والعمليات الحسابية. حجم الأعداد الصحيحة العادية هو 32 بت. يعتمد توفّر أحجام البتات الأخرى على الجهاز. تتوافق عادةً أجهزة Vulkan التي تعمل بنظام Android مع الأعداد الكاملة المكوّنة من 16 و8 بت. يحدِّد Vulkan نوعًا عدديًا بسعة 64 بت، ولكن لا يتوافق النوع بشكل شائع مع أجهزة Vulkan على Android، ولا يُنصح باستخدامه.
سلوك الدقة النصفية غير المُثلى
تجمع تصاميم وحدة المعالجة الرسومية الحديثة بين قيمتَين بسعة 16 بت في زوج بسعة 32 بت، و تنفّذ التعليمات التي تعمل على هذا الزوج. للحصول على الأداء الأمثل، تجنَّب استخدام متغيّرات عددية متسلسلة بدقة 16 بت، وحوِّل البيانات إلى متجهات من عنصرَين أو أربعة عناصر. قد يتمكّن مُجمِّع البرامج النصية للظلال من استخدام القيم السكالية في عمليات المتجهات. ومع ذلك، إذا كنت تعتمد على المُجمِّع لتحسين القيم السكالينية، عليك فحص ناتج المُجمِّع للتحقّق من استخدام تقنية التحويل إلى مصفوفات.
إنّ التحويل من النقطة العائمة بدقة 32 بت و16 بت وإليها يتطلّب تكلفة حسابية. يمكنك تقليل النفقات العامة عن طريق تقليل الإحالات الناجحة الدقيقة في الرمز البرمجي.
قياس الاختلافات في الأداء بين الإصدارَين 16 بت و32 بت من خوارزمياتك لا يؤدي استخدام الدقة النصف إلى تحسين الأداء دائمًا، لا سيما في العمليات الحسابية المعقّدة. إنّ الخوارزميات التي تستخدِم بشكلٍ كبير تعليمات جمع المتعدّد المُدمَج (FMA) على البيانات المتّجهة هي مرشّحات جيدة لتحسين الأداء بنصف الدقة.
إتاحة التنسيق الرقمي
تتوافق جميع أجهزة Vulkan على Android مع الأرقام ذات الدقة الواحدة و32 بت والتي تتضمّن فاصلة عائمة، والأرقام الصحيحة 32 بت في عمليات حساب البيانات والظلال. لا يمكن ضمان توفّر التنسيقات الأخرى، ولا يمكن ضمان ملاءمتها لجميع حالات الاستخدام في حال توفّرها.
يتيح Vulkan فئتَين من الدعم للتنسيقات الرقمية الاختيارية: الحسابي والتخزين. قبل استخدام تنسيق معيّن، تأكَّد من أنّ الجهاز متوافق معه في كلٍّ من فئتَي التوافق.
دعم العمليات الحسابية
يجب أن يعلن جهاز Vulkan عن توفّر عمليات حسابية لتنسيق رقمي لكي يكون قابلاً للاستخدام في برامج shaders. تتيح أجهزة Vulkan على Android بشكل شائع استخدام التنسيقات التالية للعمليات الحسابية:
- عدد صحيح 32 بت (إلزامي)
- قيمة عائمة بسعة 32 بت (إلزامية)
- عدد صحيح 8 بت (اختياري)
- عدد صحيح 16 بت (اختياري)
- عدد فاصل عائم بدقة منخفضة 16 بت (اختياري)
لتحديد ما إذا كان جهاز Vulkan يتوافق مع الأعداد الصحيحة 16 بت للعمليات الحسابية،
استرجع ميزات الجهاز من خلال استدعاء الدالة
vkGetPhysicalDeviceFeatures2() والتحقّق مما إذا كان الحقل
shaderInt16
في بنية النتيجة VkPhysicalDeviceFeatures2
صحيحًا.
لتحديد ما إذا كان جهاز Vulkan متوافقًا مع الأعداد العشرية 16 بت أو الأعداد الصحيحة 8 بت، اتّبِع الخطوات التالية:
- تحقَّق مما إذا كان الجهاز متوافقًا مع إضافة Vulkan VK_KHR_shader_float16_int8. يجب استخدام الإضافة لتفعيل الأرقام العشرية 16 بت والأرقام الصحيحة 8 بت.
- إذا كان
VK_KHR_shader_float16_int8
متوافقًا، يمكنك إلحاقVkPhysicalDeviceFeatures2.pNext
بسلسلةVkPhysicalDeviceFeatures2.pNext
مع تضمين المؤشر إلى بنية VkPhysicalDeviceShaderFloat16Int8Features. - تحقَّق من حقلَي
shaderFloat16
وshaderInt8
في بنية نتيجةVkPhysicalDeviceShaderFloat16Int8Features
بعد استدعاءvkGetPhysicalDeviceFeatures2()
. إذا كانت قيمة الحقل هيtrue
، يكون التنسيق متوافقًا مع العمليات الحسابية لبرنامج التظليل.
على الرغم من أنّ استخدام إضافة VK_KHR_shader_float16_int8
ليس شرطًا في Vulkan 1.1 أو ملف Android Baseline Profile لعام 2022، إلا أنّه شائع جدًا على أجهزة Android.
دعم مساحة التخزين
يجب أن يعلن جهاز Vulkan عن توافقه مع تنسيق رقمي اختياري لأنواع تخزين معيّنة. تُعلن إضافة VK_KHR_16bit_storage عن توفّر تنسيقات الأعداد الصحيحة 16 بت وتنسيقات النقطة العائمة 16 بت. تحدِّد الإضافة أربعة أنواع تخزين. يمكن أن يتيح الجهاز استخدام أرقام 16 بت لكل أنواع التخزين أو بعضها أو بعضها فقط.
أنواع مساحة التخزين هي:
- عناصر التخزين المؤقت
- عناصر ذاكرة التخزين المؤقت الموحّدة
- دفع وحدات ثابتة
- واجهات إدخال وإخراج Shader
تتوافق معظم أجهزة Vulkan 1.1 على Android مع تنسيقات 16 بت في عناصر تخزين المخزن المؤقت، ولكن ليس كلها. لا تفترض توفُّر التوافق استنادًا إلى طراز وحدة معالجة الرسومات. قد لا تتوافق الأجهزة التي تستخدم برامج تشغيل قديمة لوحدة معالجة رسومات معيّنة مع عناصر التخزين المؤقت، في حين تتوافق الأجهزة التي تستخدم برامج تشغيل أحدث مع هذه العناصر.
يعتمد بشكل عام توافق تنسيقات 16 بت في المخزن المؤقت الموحّد، ووحدات الدفع الثابتة، وshader واجهات الإدخال/الإخراج على الشركة المصنّعة لوحدة معالجة الرسومات. على نظام 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;
}
}
مستوى دقة البيانات
يمكن أن يمثّل عدد النقطة العائمة بنصف الدقة نطاقًا أصغر من القيم بدقة أقل من عدد النقطة العائمة بدقة واحدة. غالبًا ما يكون النصف الدقيق خيارًا بسيطًا وبدون فقدان الإدراك مقارنةً بالدقة الكاملة. ومع ذلك، قد لا تكون الدقة النصفوية عملية في جميع حالات الاستخدام. بالنسبة إلى بعض أنواع البيانات، يمكن أن يؤدي تقليل النطاق والدقة إلى حدوث عيوب في الرسم البياني أو عرض غير صحيح.
تشمل أنواع البيانات التي تكون مرشحة بشكل جيد للتمثيل بالتنسيق الثنائي الدقة النقطة العائمة ما يلي:
- بيانات الموقع الجغرافي في إحداثيات الفضاء المحلي
- إحداثيات UV للنقوش الصغيرة التي تتضمّن لفّ UV محدودًا يمكن تقييده بنطاق إحداثيات من -1.0 إلى 1.0
- بيانات السطح العادي والمماسي والمماسي الثنائي
- بيانات ألوان الرؤوس
- البيانات التي تتطلّب دقة منخفضة وتتمحور حول 0.0
في ما يلي أنواع البيانات التي لا ننصح بتمثيلها باستخدام الأرقام العشرية ذات الدقة النصف:
- بيانات الموقع الجغرافي في إحداثيات العالم العالمية
- إحداثيات UV للزخرفة لحالات الاستخدام العالية الدقة، مثل إحداثيات عناصر واجهة المستخدم في جدول الخريطة
الدقة في رمز برنامج التظليل
تتيح لغتا برمجة OpenGL Shading Language (GLSL) وHigh-level Shader Language (HLSL) تحديد دقة relaxed أو دقة صريحة للأنواع الرقمية. يتم التعامل مع الدقة المنخفضة كاقتراح لمجمِّع shaders. الدقة الصريحة هي أحد متطلبات الدقة المحدّدة. تستخدم أجهزة Vulkan على Android بشكل عام تنسيقات 16 بت عند اقتراح دقة أقل. قد تتجاهل أجهزة Vulkan الأخرى، خاصةً أجهزة الكمبيوتر المكتبي التي تستخدم أجهزة رسومات لا تتيح استخدام التنسيقات بسعة 16 بت، الدقة المنخفضة وتستمر في استخدام التنسيقات بسعة 32 بت.
ملحقات التخزين في GLSL
يجب تحديد إضافات GLSL المناسبة لتفعيل التنسيقات الرقمية بسعة 16 أو 8 بت في بنية التخزين وبنية المخزن المؤقت الموحّد. في ما يلي إقرارات التمديد ذات الصلة:
// 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
لعدد عائم بنصف دقة.
تفسِّر برامج التحويل GLSL لـ Vulkan العنصر المحدِّد lowp
القديم على أنّه mediump
.
في ما يلي بعض الأمثلة على الدقة المنخفضة:
mediump vec4 my_vector; // Suggest 16-bit half precision
highp mat4 my_matrix; // Suggest 32-bit single precision
الدقة الصريحة في GLSL
أدرِج الإضافة GL_EXT_shader_explicit_arithmetic_types_float16
في رمز GLSL
لتفعيل استخدام أنواع النقطة العائمة بسعة 16 بت:
#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require
يمكنك تحديد أنواع المتجهات والمصفوفات والكميات غير المتغيرة بنقطة عائمة 16 بت في GLSL باستخدام الكلمات الرئيسية التالية:
float16_t f16vec2 f16vec3 f16vec4
f16mat2 f16mat3 f16mat4
f16mat2x2 f16mat2x3 f16mat2x4
f16mat3x2 f16mat3x3 f16mat3x4
f16mat4x2 f16mat4x3 f16mat4x4
يمكنك تحديد أنواع عدد صحيح 16 بت وأنواع متجهات في GLSL باستخدام الكلمات الرئيسية التالية:
int16_t i16vec2 i16vec3 i16vec4
uint16_t u16vec2 u16vec3 u16vec4
دقة مُخفَّضة في HLSL
يستخدم لغة HLSL عبارة الحد الأدنى من الدقة بدلاً من الدقة المُخفَّضة. تحدّد الكلمة الرئيسية لنوع الدقة الأدنى الحد الأدنى للدقة، ولكن قد يستبدل المُجمِّع دقة أعلى إذا كانت الدقة الأعلى خيارًا أفضل للأجهزة المستهدفة. يتم تحديد عدد صحيح 16 بت بدقة منخفضة باستخدام الكلمة الرئيسية
min16float
. يتم تحديد الأعداد الصحيحة ذات الدقة الدنيا والموقَّعة وغير الموقَّعة بسعة 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;
-enable-16bit-types