לפורמט המספרי של נתונים גרפיים וחישובים של תוכנת ההצללה (shader) יכולה משפיעה באופן משמעותי על ביצועי המשחק.
פורמטים אופטימליים משמשים לביצוע הפעולות הבאות:
- הגברת היעילות של השימוש במטמון GPU
- הפחתת הצריכה של רוחב הפס בזיכרון, חיסכון בחשמל והגברת הביצועים
- מיקסום התפוקה החישובית בתוכניות הצללה (shader)
- צמצום השימוש בזיכרון ה-RAM במעבד (CPU) במשחק
פורמטים של נקודה צפה (floating-point)
רוב החישובים והנתונים בגרפיקה תלת-ממדית מודרנית משתמשים בנקודה צפה . ב-Vulkan ב-Android משתמשים במספרי נקודה צפה (floating-point) שהם 32 או בגודל 16 ביט. מספר נקודה צפה (floating-point) של 32 סיביות נקרא דיוק יחיד או דיוק מלא; מספר נקודה צפה (floating-point) של 16 ביט המדויק.
ב-Vulkan מגדירים סוג נקודה צפה (floating-point) של 64 ביט, אך הסוג הזה לא נפוץ נתמך על ידי מכשירי Vulkan ב-Android, והשימוש בו לא מומלץ. A 64-bit מספר הנקודה הצפה נקרא בדרך כלל דיוק כפול.
פורמטים של מספר שלם
מספרים שלמים חתומים ולא חתומים משמשים גם לנתונים ולחישובים. הגודל הסטנדרטי של מספר שלם הוא 32 סיביות. התמיכה בגדלים אחרים של ביטים היא במכשיר תלויה. מכשירי Vulkan עם Android בדרך כלל תומכים ב-16 ביט וב-8 ביט שלמים. ב-Vulkan מגדירים סוג מספר שלם של 64 ביט, אך הסוג הזה אינו נפוץ נתמך על ידי מכשירי Vulkan ב-Android, והשימוש בו לא מומלץ.
התנהגות לא אופטימלית של חצי דיוק
ארכיטקטורות GPU מודרניות משלבות שני ערכים של 16 ביט יחד בצמד של 32 ביט. להטמיע את ההוראות שפועלות על הצמד. כדי ליהנות מביצועים אופטימליים, מומלץ להימנע שימוש במשתנים צפים סקלריים של 16 ביט; להפוך נתונים לשניים או ארבעה רכיבים של שאילתות. המהדר של תוכנת ההצללה (shader) עשוי להשתמש בערכים סקלריים בווקטור ב-AI. עם זאת, אם מסתמכים על המהדר כדי לבצע אופטימיזציה של סקלרים, את פלט המהדר כדי לאמת וקטורים.
להמרה לנקודה צפה של 32 סיביות ו-16 סיביות ולנקודה צפה (floating-point) עלות חישובית. צמצום התקורה על ידי צמצום ההמרות המדויקות
ההבדלים בביצועים בין גרסת 16 סיביות לגרסת 32 סיביות של אלגוריתמים. חצי דיוק לא תמיד מוביל לשיפור בביצועים, במיוחד לצורך חישובים מורכבים. אלגוריתמים שעושים שימוש נרחב ב-Fused הוראות להכפלה (FMA) לגבי נתונים וקטוריים הן מועמדים טובים ביצועים משופרים בחצי דיוק.
תמיכה בפורמט מספרי
כל מכשירי Vulkan ב-Android תומכים בנקודה צפה ברמת דיוק יחידה של 32 ביט ומספרים שלמים של 32 ביט בחישובים של נתונים ותוכנת ההצללה. תמיכה עבור לא מובטח שפורמטים אחרים יהיו זמינים, ואם הם זמינים, לא מובטח לכל התרחישים לדוגמה.
ב-Vulkan יש שתי קטגוריות של תמיכה בפורמטים מספריים אופציונליים: אריתמטי ואחסון. לפני שמשתמשים בפורמט ספציפי, צריך לוודא שהמכשיר תומך בו קטגוריות.
תמיכה בחשבון
מכשיר Vulkan צריך להצהיר על תמיכה אריתמטית בפורמט מספרי כדי שהוא להיות שימושי לתוכנות הצללה (shader). בדרך כלל, מכשירי Vulkan ב-Android תומכים ב את הפורמטים הבאים לחשבון:
- מספר שלם בגרסת 32 ביט (חובה)
- נקודה צפה (floating-point) של 32 ביט
- מספר שלם בגרסת 8 ביט (אופציונלי)
- מספר שלם בגרסת 16 ביט (אופציונלי)
- נקודה צפה (floating-point) של 16 ביט (אופציונלי)
כדי לקבוע אם מכשיר Vulkan תומך במספרים שלמים של 16 ביט לחישוב:
לאחזר את תכונות המכשיר על ידי קריאה
הפונקציה vkGetTechnicalDeviceFeatures2() ובדיקה אם
השדה shaderInt16
ברשימה Vk דיגיטלייםDeviceFeatures2
הוא TRUE.
כדי לקבוע אם מכשיר Vulkan תומך במספרים צפים של 16 ביט או במספרים שלמים של 8 ביט, מבצעים את השלבים הבאים:
- בודקים אם המכשיר תומך תוסף VK_KHR_shader_float16_int8 של Vulkan. התוסף הוא הנדרש לתמיכה במספרים צפים ב-16 סיביות ובמספרים שלמים ב-8 ביט.
- אם המדיניות
VK_KHR_shader_float16_int8
נתמכת, צריך לצרף מצביע על המבנה Vk דיגיטלייםDeviceShaderFloat16Int8Features לרשתVkPhysicalDeviceFeatures2.pNext
. - צריך לבדוק את השדות
shaderFloat16
ו-shaderInt8
מבנה התוצאה שלVkPhysicalDeviceShaderFloat16Int8Features
אחרי השיחהvkGetPhysicalDeviceFeatures2()
אם הערך בשדה הואtrue
, הפורמט הוא נתמך בתוכנית של תוכנת ההצללה.
אומנם זו לא דרישה ב-Vulkan 1.1 או שנת 2022
פרופיל Android Baseline, תמיכה ב-VK_KHR_shader_float16_int8
מאוד נפוץ במכשירי Android.
תמיכה בנפח האחסון
מכשיר Vulkan צריך להצהיר על תמיכה בפורמט מספרי אופציונלי בסוגי אחסון מסוימים. התוסף VK_KHR_16bit_storage החברה מצהירה על תמיכה במספר שלם של 16 ביט ובפורמטים של נקודה צפה (floating-point) של 16 ביט. ארבעה מוצרים סוגי האחסון מוגדרים על ידי התוסף. מכשיר יכול לתמוך במספרי 16 ביט ללא, עבור חלק או עבור כל סוגי האחסון.
סוגי האחסון הם:
- אובייקטים של מאגר נתונים זמני של אחסון
- אובייקטים אחידים של מאגר נתונים זמני
- דחיפת בלוקים קבועים
- ממשקי קלט ופלט של Shader
רוב מכשירי Vulkan 1.1 ב-Android תומכים גם בפורמטים של 16 ביט, אבל לא בכולם. למאגר הנתונים הזמני. אל תניח שיש תמיכה על סמך מודל ה-GPU. מכשירים עם מנהלי התקנים ישנים יותר ל-GPU מסוים, יכול להיות שלא תומכים באובייקטים של מאגר נתונים זמני של אחסון, ואילו מכשירים עם מנהלי התקנים חדשים יותר כן.
תמיכה בפורמטים של 16 ביט במאגרי נתונים זמניים אחידים, בלוקים קבועים ותוכנת הצללה (shader) בדרך כלל ממשקי הקלט/פלט תלויים ביצרן ה-GPU. במצב מופעל ב-Android, יחידת 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;
}
}
רמת הדיוק של הנתונים
מספר נקודה צפה בחצי דיוק יכול לייצג טווח ערכים קטן יותר ברמת דיוק נמוכה יותר ממספר נקודה צפה עם דיוק יחיד. 'חצי דיוק' הוא לרוב בחירה פשוטה, שנראית ללא אובדן בדיוק יחיד. עם זאת, ייתכן שחצי דיוק לא יהיה מעשי בכל תרחישי השימוש. בסוגים מסוימים של נתונים, הטווח המופחת והדיוק עלולים להוביל ליצירת גרפיקה ארטיפקטים או עיבוד שגוי.
סוגי נתונים שיכולים להתאים לייצוג בחצי דיוק נקודה צפה (floating-point):
- נתוני מיקום בקואורדינטות של המרחב המקומי
- קרינת UV של טקסטורה למרקמים קטנים יותר עם גלישת UV מוגבלת מוגבל לטווח קואורדינטות -1.0 עד 1.0
- נתונים רגילים, משיקים ביטנגנסים
- נתוני צבע של קודקוד
- נתונים עם דרישות ברמת דיוק נמוכה, מתמקדים ב-0.0
סוגי נתונים שלא מומלץ לייצוג בהם בציפה בעל דיוק חצי דיוק כוללים:
- נתוני מיקום בקואורדינטות עולמיות
- קרינת UV עם מרקם עבור תרחישים לדוגמה ברמת דיוק גבוהה, כמו קואורדינטות של רכיבי ממשק משתמש גיליון אטלס
דיוק בקוד של תוכנת ההצללה
את OpenGL Shading Language (GLSL) וכן כלי להצללה (shader) ברמה גבוהה (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
לפני סוג של נקודה צפה (floating-point) כדי להציע
מספר ממשי (float) בעל דיוק יחיד, ותוחם 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 כדי לאפשר שימוש בסוגי נקודה צפה (floating-point) של 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 משתמש במונח דיוק מינימלי במקום ברמת דיוק מקלה. מינימלית
מילת מפתח מסוג precision מציינת את הדיוק המינימלי, אך המהדר יכול
להחליף דיוק גבוה יותר אם רמת דיוק גבוהה יותר היא אפשרות טובה יותר
חומרת היעד. ערך צף מדויק מינימלי של 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;