يمكن أن يكون للتنسيق الرقمي لبيانات الرسومات وعمليات حساب تظليل الألوان تأثير كبير في أداء لعبتك.
تؤدي التنسيقات المثالية ما يلي:
- زيادة كفاءة استخدام ذاكرة التخزين المؤقت لوحدة معالجة الرسومات
- تقليل استهلاك معدل نقل البيانات في الذاكرة، ما يوفّر الطاقة ويزيد من الأداء
- زيادة معدل النقل الحسابي في برامج التظليل
- تقليل استخدام ذاكرة الوصول العشوائي (RAM) لوحدة المعالجة المركزية (CPU) في لعبتك
تنسيقات الفاصلة العائمة
تستخدم معظم العمليات الحسابية والبيانات في الرسومات الثلاثية الأبعاد الحديثة أرقام الفاصلة العائمة. يستخدم 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 عن الدعم الحسابي لتنسيق رقمي لكي يكون قابلاً للاستخدام في برامج التظليل. تدعم أجهزة Vulkan على Android عادةً التنسيقات التالية للعمليات الحسابية:
- عدد صحيح 32 بت (إلزامي)
- نقطة عائمة 32 بت (إلزامي)
- عدد صحيح من 8 بت (اختياري)
- عدد صحيح من 16 بت (اختياري)
- نقطة عائمة بدقة نصفية تبلغ 16 بت (اختياري)
لتحديد ما إذا كان جهاز Vulkan يتيح استخدام الأعداد الصحيحة ذات الـ 16 بت في العمليات الحسابية،
يمكنك استرداد ميزات الجهاز من خلال استدعاء الدالة
vkGetPhysicalDeviceFeatures2() والتحقّق مما إذا كان
الحقل shaderInt16 في بنية النتيجة VkPhysicalDeviceFeatures2
صحيحًا.
لتحديد ما إذا كان جهاز Vulkan يتيح استخدام الأعداد العشرية ذات الدقة النصفية أو الأعداد الصحيحة ذات الدقة الربعية، اتّبِع الخطوات التالية:
- تحقَّق مما إذا كان الجهاز متوافقًا مع إضافة Vulkan VK_KHR_shader_float16_int8. يجب استخدام هذه الإضافة لتوفير الدعم للأعداد العشرية ذات الدقة النصفية (16 بت) والأعداد الصحيحة ذات الدقة المنخفضة (8 بت).
- إذا كان
VK_KHR_shader_float16_int8متاحًا، أضِف مؤشر بنية VkPhysicalDeviceShaderFloat16Int8Features إلى سلسلةVkPhysicalDeviceFeatures2.pNext. - تحقَّق من الحقلَين
shaderFloat16وshaderInt8في بنية النتيجةVkPhysicalDeviceShaderFloat16Int8Featuresبعد استدعاءvkGetPhysicalDeviceFeatures2(). إذا كانت قيمة الحقل هيtrue، يكون التنسيق متوافقًا مع العمليات الحسابية لبرنامج التظليل.
على الرغم من أنّ إضافة VK_KHR_shader_float16_int8 ليست من متطلبات Vulkan 1.1 أو الملف الشخصي الأساسي لنظام التشغيل Android لعام 2022، فإنّ إتاحتها أمر شائع جدًا على أجهزة Android.
الدعم بشأن مساحة التخزين
يجب أن يعلن جهاز Vulkan عن توافقه مع تنسيق رقمي اختياري لأنواع معيّنة من مساحات التخزين. تتيح إضافة VK_KHR_16bit_storage استخدام تنسيقات الأعداد الصحيحة ذات 16 بت وتنسيقات النقطة العائمة ذات 16 بت. يتم تحديد أربعة أنواع من مساحات التخزين من خلال الإضافة. يمكن أن يتيح الجهاز استخدام أرقام 16 بت لبعض أنواع مساحة التخزين أو جميعها أو لا يتيحها على الإطلاق.
أنواع مساحة التخزين هي:
- عناصر مخزن التخزين المؤقت
- عناصر المخزن المؤقت الموحَّدة
- الضغط على الوحدات الأساسية الثابتة
- واجهات الإدخال والإخراج الخاصة ببرامج التظليل
تتيح معظم أجهزة Vulkan 1.1 على Android، وليس جميعها، تنسيقات 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;
}
}
مستوى دقة البيانات
يمكن أن يمثّل رقم النقطة العائمة بنصف الدقة نطاقًا أصغر من القيم بدقة أقل من رقم النقطة العائمة بدقة أحادية. غالبًا ما تكون الدقة النصفية خيارًا بسيطًا وبدون فقدان البيانات مقارنةً بالدقة الأحادية. ومع ذلك، قد لا تكون الدقة النصفية عملية في جميع حالات الاستخدام. بالنسبة إلى بعض أنواع البيانات، يمكن أن يؤدي النطاق والدقة المنخفضان إلى ظهور عناصر رسومية أو عرض غير صحيح.
تشمل أنواع البيانات التي تُعدّ مناسبة للتمثيل في الفاصلة العائمة بنصف الدقة ما يلي:
- بيانات الموقع الجغرافي في إحداثيات المساحة المحلية
- إحداثيات UV للأنسجة الأصغر حجمًا مع التفاف UV محدود يمكن تقييده بنطاق إحداثيات من -1.0 إلى 1.0
- بيانات المتجه العمودي والمماس والمماس العمودي
- بيانات ألوان الرؤوس
- بيانات ذات متطلبات دقة منخفضة تتمحور حول 0.0
تشمل أنواع البيانات التي لا يُنصح بتمثيلها في أعداد عائمة بنصف الدقة ما يلي:
- بيانات الموقع الجغرافي في الإحداثيات العالمية
- إحداثيات UV للزخرفة لحالات الاستخدام عالية الدقة، مثل إحداثيات عناصر واجهة المستخدم في ورقة أطلس
الدقة في رمز التظليل
تتيح لغات برمجة تظليل OpenGL Shading Language (GLSL) وHigh-level Shader Language (HLSL) تحديد الدقة المرنة أو الدقة الصريحة للأنواع الرقمية. يتم التعامل مع الدقة المريحة كتوصية لمجمّع التظليل. الدقة الصريحة هي أحد متطلبات الدقة المحددة. تستخدم أجهزة 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;