فرمت عددی داده های گرافیکی و محاسبات شیدر می تواند تاثیر بسزایی در عملکرد بازی شما داشته باشد.
فرمت های بهینه موارد زیر را انجام می دهند:
- افزایش کارایی استفاده از حافظه پنهان GPU
- کاهش مصرف پهنای باند حافظه، صرفه جویی در مصرف انرژی و افزایش عملکرد
- به حداکثر رساندن توان محاسباتی در برنامه های سایه زن
- استفاده از رم پردازنده را در بازی خود به حداقل برسانید
فرمت های ممیز شناور
اکثر محاسبات و داده ها در گرافیک سه بعدی مدرن از اعداد ممیز شناور استفاده می کنند. Vulkan در اندروید از اعداد ممیز شناور استفاده می کند که اندازه آنها 32 یا 16 بیت است. یک عدد ممیز شناور 32 بیتی معمولاً با دقت تک یا دقت کامل شناخته می شود. یک عدد ممیز شناور 16 بیتی، نیم دقت.
Vulkan یک نوع ممیز شناور 64 بیتی تعریف می کند، اما این نوع معمولاً توسط دستگاه های Vulkan در اندروید پشتیبانی نمی شود و استفاده از آن توصیه نمی شود. یک عدد ممیز شناور 64 بیتی معمولا به عنوان دقت مضاعف شناخته می شود.
فرمت های عدد صحیح
از اعداد صحیح علامت دار و بدون علامت نیز برای داده ها و محاسبات استفاده می شود. اندازه عدد صحیح استاندارد 32 بیت است. پشتیبانی از سایر اندازه های بیت به دستگاه بستگی دارد. دستگاه های Vulkan دارای اندروید معمولاً از اعداد صحیح 16 بیتی و 8 بیتی پشتیبانی می کنند. Vulkan یک نوع عدد صحیح 64 بیتی را تعریف می کند، اما این نوع معمولاً توسط دستگاه های Vulkan در اندروید پشتیبانی نمی شود و استفاده از آن توصیه نمی شود.
رفتار نیمه دقیق نابهینه
معماریهای مدرن GPU دو مقدار 16 بیتی را با هم در یک جفت 32 بیتی ترکیب میکنند و دستورالعملهایی را پیادهسازی میکنند که روی این جفت کار میکنند. برای عملکرد بهینه، از استفاده از متغیرهای شناور اسکالر 16 بیتی خودداری کنید. بردار کردن داده ها به بردارهای دو یا چهار عنصری. کامپایلر سایه زن ممکن است بتواند از مقادیر اسکالر در عملیات برداری استفاده کند. با این حال، اگر برای بهینهسازی اسکالرها به کامپایلر تکیه میکنید، خروجی کامپایلر را برای تأیید بردارسازی بررسی کنید.
تبدیل به و از ممیز شناور دقیق 32 بیتی و 16 بیتی هزینه محاسباتی دارد. با به حداقل رساندن تبدیل های دقیق در کد خود، هزینه های اضافی را کاهش دهید.
تفاوت عملکرد بین نسخه های 16 بیتی و 32 بیتی الگوریتم های خود را معیار قرار دهید. نیم دقت همیشه منجر به بهبود عملکرد نمی شود، به خصوص برای محاسبات پیچیده. الگوریتمهایی که از دستورالعملهای ضربافزودن ذوب شده (FMA) روی دادههای بردار استفاده میکنند، کاندیدای خوبی برای بهبود عملکرد با دقت نیمی هستند.
پشتیبانی از فرمت عددی
همه دستگاههای Vulkan در اندروید از اعداد ممیز شناور تک دقیق، ۳۲ بیتی و اعداد صحیح ۳۲ بیتی در محاسبات دادهها و سایهبان پشتیبانی میکنند. پشتیبانی از فرمت های دیگر تضمین نمی شود که در دسترس باشد و در صورت موجود بودن، برای همه موارد استفاده تضمین نمی شود.
Vulkan دارای دو دسته پشتیبانی از فرمت های عددی اختیاری است: حسابی و ذخیره سازی. قبل از استفاده از یک فرمت خاص، مطمئن شوید که یک دستگاه آن را در هر دو دسته پشتیبانی می کند.
پشتیبانی حسابی
یک دستگاه Vulkan باید پشتیبانی حسابی را برای یک قالب عددی اعلام کند تا در برنامه های سایه زن قابل استفاده باشد. دستگاههای Vulkan در اندروید معمولاً از فرمتهای زیر برای محاسبات پشتیبانی میکنند:
- عدد صحیح 32 بیتی (اجباری)
- ممیز شناور 32 بیتی (اجباری)
- عدد صحیح 8 بیتی (اختیاری)
- عدد صحیح 16 بیتی (اختیاری)
- ممیز شناور نیمه دقیق 16 بیتی (اختیاری)
برای تعیین اینکه آیا یک دستگاه Vulkan از اعداد صحیح 16 بیتی برای محاسبات پشتیبانی می کند یا خیر، ویژگی های دستگاه را با فراخوانی تابع vkGetPhysicalDeviceFeatures2() بازیابی کنید و بررسی کنید که آیا فیلد shaderInt16
در ساختار نتیجه VkPhysicalDeviceFeatures2 درست است یا خیر.
برای تعیین اینکه آیا دستگاه Vulkan از شناورهای 16 بیتی یا اعداد صحیح 8 بیتی پشتیبانی می کند، مراحل زیر را انجام دهید:
- بررسی کنید که آیا دستگاه از افزونه VK_KHR_shader_float16_int8 Vulkan پشتیبانی می کند یا خیر. پسوند برای پشتیبانی 16 بیتی شناور و اعداد صحیح 8 بیتی مورد نیاز است.
- اگر
VK_KHR_shader_float16_int8
پشتیبانی میشود، یک نشانگر ساختار VkPhysicalDeviceShaderFloat16Int8Features را به زنجیرهVkPhysicalDeviceFeatures2.pNext
اضافه کنید. - پس از فراخوانی
vkGetPhysicalDeviceFeatures2()
فیلدهایshaderFloat16
وshaderInt8
ساختار نتیجهVkPhysicalDeviceShaderFloat16Int8Features
را بررسی کنید. اگر مقدار فیلدtrue
باشد، قالب برای محاسبات برنامه سایه زن پشتیبانی می شود.
اگرچه در Vulkan 1.1 یا نمایه Android Baseline 2022 الزامی نیست، پشتیبانی از افزونه VK_KHR_shader_float16_int8
در دستگاههای Android بسیار رایج است.
پشتیبانی ذخیره سازی
یک دستگاه Vulkan باید از یک قالب عددی اختیاری برای انواع ذخیره سازی خاص پشتیبانی کند. برنامه افزودنی VK_KHR_16bit_storage پشتیبانی از فرمت های اعداد صحیح 16 بیتی و ممیز شناور 16 بیتی را اعلام می کند. چهار نوع ذخیره سازی توسط افزونه تعریف شده است. یک دستگاه می تواند اعداد 16 بیتی را برای هیچ، برخی یا همه انواع حافظه پشتیبانی کند.
انواع ذخیره سازی عبارتند از:
- ذخیره سازی اشیاء بافر
- اشیاء بافر یکنواخت
- بلوک های ثابت را فشار دهید
- رابط های ورودی و خروجی سایه زن
بیشتر، اما نه همه، دستگاه های Vulkan 1.1 در اندروید از فرمت های 16 بیتی در اشیاء بافر ذخیره سازی پشتیبانی می کنند. پشتیبانی بر اساس مدل GPU را فرض نکنید. ممکن است دستگاههایی با درایورهای قدیمیتر برای یک GPU معین، از اشیاء بافر ذخیرهسازی پشتیبانی نکنند، در حالی که دستگاههایی با درایورهای جدیدتر این کار را انجام میدهند.
پشتیبانی از فرمتهای 16 بیتی در بافرهای یکنواخت، بلوکهای ثابت فشاری و رابطهای ورودی/خروجی سایهزن معمولاً به سازنده GPU وابسته است. در اندروید، یک 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;
}
}
سطح دقیق برای داده ها
یک عدد ممیز شناور با نیم دقت می تواند محدوده کوچکتری از مقادیر را با دقت کمتری نسبت به یک عدد ممیز شناور تک دقیق نشان دهد. نیمه دقیق اغلب یک انتخاب ساده و بدون تلفات ادراکی نسبت به تک دقت است. با این حال، نیم دقت ممکن است در همه موارد استفاده عملی نباشد. برای برخی از انواع داده ها، کاهش دامنه و دقت می تواند منجر به مصنوعات گرافیکی یا ارائه نادرست شود.
انواع داده هایی که کاندیدهای خوبی برای نمایش در ممیز شناور نیمه دقیق هستند عبارتند از:
- داده ها را در مختصات فضای محلی قرار دهید
- UVs بافت برای بافت های کوچکتر با پوشش محدود UV که می تواند در محدوده مختصات -1.0 تا 1.0 محدود شود.
- داده های نرمال، مماس و دو تانژانت
- داده های رنگ راس
- دادههایی با نیازهای دقت پایین با محوریت 0.0
انواع داده هایی که برای نمایش در شناور نیمه دقیق توصیه نمی شوند عبارتند از:
- موقعیت داده ها در مختصات جهانی جهانی
- UVs بافت برای موارد استفاده با دقت بالا مانند مختصات عنصر UI در یک صفحه اطلس
دقت در کد سایه زن
زبانهای برنامهنویسی سایهزن OpenGL Shading Language (GLSL) و زبان Shader سطح بالا (HLSL) از مشخصات دقت آرام یا دقت صریح برای انواع عددی پشتیبانی میکنند. دقت آرام به عنوان یک توصیه برای کامپایلر سایه زن در نظر گرفته می شود. دقت صریح لازمه دقت مشخص شده است. دستگاههای Vulkan در اندروید معمولاً از فرمتهای 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;
فرمت عددی داده های گرافیکی و محاسبات شیدر می تواند تاثیر بسزایی در عملکرد بازی شما داشته باشد.
فرمت های بهینه موارد زیر را انجام می دهند:
- افزایش کارایی استفاده از حافظه پنهان GPU
- کاهش مصرف پهنای باند حافظه، صرفه جویی در مصرف انرژی و افزایش عملکرد
- به حداکثر رساندن توان محاسباتی در برنامه های سایه زن
- استفاده از رم پردازنده را در بازی خود به حداقل برسانید
فرمت های ممیز شناور
اکثر محاسبات و داده ها در گرافیک سه بعدی مدرن از اعداد ممیز شناور استفاده می کنند. Vulkan در اندروید از اعداد ممیز شناور استفاده می کند که اندازه آنها 32 یا 16 بیت است. یک عدد ممیز شناور 32 بیتی معمولاً با دقت تک یا دقت کامل شناخته می شود. یک عدد ممیز شناور 16 بیتی، نیم دقت.
Vulkan یک نوع ممیز شناور 64 بیتی تعریف می کند، اما این نوع معمولاً توسط دستگاه های Vulkan در اندروید پشتیبانی نمی شود و استفاده از آن توصیه نمی شود. یک عدد ممیز شناور 64 بیتی معمولا به عنوان دقت مضاعف شناخته می شود.
فرمت های عدد صحیح
از اعداد صحیح علامت دار و بدون علامت نیز برای داده ها و محاسبات استفاده می شود. اندازه عدد صحیح استاندارد 32 بیت است. پشتیبانی از سایر اندازه های بیت به دستگاه بستگی دارد. دستگاه های Vulkan دارای اندروید معمولاً از اعداد صحیح 16 بیتی و 8 بیتی پشتیبانی می کنند. Vulkan یک نوع عدد صحیح 64 بیتی را تعریف می کند، اما این نوع معمولاً توسط دستگاه های Vulkan در اندروید پشتیبانی نمی شود و استفاده از آن توصیه نمی شود.
رفتار نیمه دقیق نابهینه
معماریهای مدرن GPU دو مقدار 16 بیتی را با هم در یک جفت 32 بیتی ترکیب میکنند و دستورالعملهایی را پیادهسازی میکنند که روی این جفت کار میکنند. برای عملکرد بهینه، از استفاده از متغیرهای شناور اسکالر 16 بیتی خودداری کنید. بردار کردن داده ها به بردارهای دو یا چهار عنصری. کامپایلر سایه زن ممکن است بتواند از مقادیر اسکالر در عملیات برداری استفاده کند. با این حال، اگر برای بهینهسازی اسکالرها به کامپایلر تکیه میکنید، خروجی کامپایلر را برای تأیید بردارسازی بررسی کنید.
تبدیل به و از ممیز شناور دقیق 32 بیتی و 16 بیتی هزینه محاسباتی دارد. با به حداقل رساندن تبدیل های دقیق در کد خود، هزینه های اضافی را کاهش دهید.
تفاوت عملکرد بین نسخه های 16 بیتی و 32 بیتی الگوریتم های خود را معیار قرار دهید. نیم دقت همیشه منجر به بهبود عملکرد نمی شود، به خصوص برای محاسبات پیچیده. الگوریتمهایی که از دستورالعملهای ضربافزودن ذوب شده (FMA) روی دادههای بردار استفاده میکنند، کاندیدای خوبی برای بهبود عملکرد با دقت نیمی هستند.
پشتیبانی از فرمت عددی
همه دستگاههای Vulkan در اندروید از اعداد ممیز شناور تک دقیق، ۳۲ بیتی و اعداد صحیح ۳۲ بیتی در محاسبات دادهها و سایهبان پشتیبانی میکنند. پشتیبانی از فرمت های دیگر تضمین نمی شود که در دسترس باشد و در صورت موجود بودن، برای همه موارد استفاده تضمین نمی شود.
Vulkan دارای دو دسته پشتیبانی از فرمت های عددی اختیاری است: حسابی و ذخیره سازی. قبل از استفاده از یک فرمت خاص، مطمئن شوید که یک دستگاه آن را در هر دو دسته پشتیبانی می کند.
پشتیبانی حسابی
یک دستگاه Vulkan باید پشتیبانی حسابی را برای یک قالب عددی اعلام کند تا در برنامه های سایه زن قابل استفاده باشد. دستگاههای Vulkan در اندروید معمولاً از فرمتهای زیر برای محاسبات پشتیبانی میکنند:
- عدد صحیح 32 بیتی (اجباری)
- ممیز شناور 32 بیتی (اجباری)
- عدد صحیح 8 بیتی (اختیاری)
- عدد صحیح 16 بیتی (اختیاری)
- ممیز شناور نیمه دقیق 16 بیتی (اختیاری)
برای تعیین اینکه آیا یک دستگاه Vulkan از اعداد صحیح 16 بیتی برای محاسبات پشتیبانی می کند یا خیر، ویژگی های دستگاه را با فراخوانی تابع vkGetPhysicalDeviceFeatures2() بازیابی کنید و بررسی کنید که آیا فیلد shaderInt16
در ساختار نتیجه VkPhysicalDeviceFeatures2 درست است یا خیر.
برای تعیین اینکه آیا دستگاه Vulkan از شناورهای 16 بیتی یا اعداد صحیح 8 بیتی پشتیبانی می کند، مراحل زیر را انجام دهید:
- بررسی کنید که آیا دستگاه از افزونه VK_KHR_shader_float16_int8 Vulkan پشتیبانی می کند یا خیر. پسوند برای پشتیبانی 16 بیتی شناور و اعداد صحیح 8 بیتی مورد نیاز است.
- اگر
VK_KHR_shader_float16_int8
پشتیبانی میشود، یک نشانگر ساختار VkPhysicalDeviceShaderFloat16Int8Features را به زنجیرهVkPhysicalDeviceFeatures2.pNext
اضافه کنید. - پس از فراخوانی
vkGetPhysicalDeviceFeatures2()
فیلدهایshaderFloat16
وshaderInt8
ساختار نتیجهVkPhysicalDeviceShaderFloat16Int8Features
را بررسی کنید. اگر مقدار فیلدtrue
باشد، قالب برای محاسبات برنامه سایه زن پشتیبانی می شود.
اگرچه در Vulkan 1.1 یا نمایه Android Baseline 2022 الزامی نیست، پشتیبانی از افزونه VK_KHR_shader_float16_int8
در دستگاههای Android بسیار رایج است.
پشتیبانی ذخیره سازی
یک دستگاه Vulkan باید از یک قالب عددی اختیاری برای انواع ذخیره سازی خاص پشتیبانی کند. برنامه افزودنی VK_KHR_16bit_storage پشتیبانی از فرمت های اعداد صحیح 16 بیتی و ممیز شناور 16 بیتی را اعلام می کند. چهار نوع ذخیره سازی توسط افزونه تعریف شده است. یک دستگاه می تواند اعداد 16 بیتی را برای هیچ، برخی یا همه انواع حافظه پشتیبانی کند.
انواع ذخیره سازی عبارتند از:
- ذخیره سازی اشیاء بافر
- اشیاء بافر یکنواخت
- بلوک های ثابت را فشار دهید
- رابط های ورودی و خروجی سایه زن
بیشتر، اما نه همه، دستگاه های Vulkan 1.1 در اندروید از فرمت های 16 بیتی در اشیاء بافر ذخیره سازی پشتیبانی می کنند. پشتیبانی بر اساس مدل GPU را فرض نکنید. ممکن است دستگاههایی با درایورهای قدیمیتر برای یک GPU معین، از اشیاء بافر ذخیرهسازی پشتیبانی نکنند، در حالی که دستگاههایی با درایورهای جدیدتر این کار را انجام میدهند.
پشتیبانی از فرمتهای 16 بیتی در بافرهای یکنواخت، بلوکهای ثابت فشاری و رابطهای ورودی/خروجی سایهزن معمولاً به سازنده GPU وابسته است. در اندروید، یک 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;
}
}
سطح دقیق برای داده ها
یک عدد ممیز شناور با نیم دقت می تواند محدوده کوچکتری از مقادیر را با دقت کمتری نسبت به یک عدد ممیز شناور تک دقیق نشان دهد. نیمه دقیق اغلب یک انتخاب ساده و بدون تلفات ادراکی نسبت به تک دقت است. با این حال، نیم دقت ممکن است در همه موارد استفاده عملی نباشد. برای برخی از انواع داده ها، کاهش دامنه و دقت می تواند منجر به مصنوعات گرافیکی یا ارائه نادرست شود.
انواع داده هایی که کاندیدهای خوبی برای نمایش در ممیز شناور نیمه دقیق هستند عبارتند از:
- داده ها را در مختصات فضای محلی قرار دهید
- UVs بافت برای بافت های کوچکتر با پوشش محدود UV که می تواند در محدوده مختصات -1.0 تا 1.0 محدود شود.
- داده های نرمال، مماس و دو تانژانت
- داده های رنگ راس
- دادههایی با نیازهای دقت پایین با محوریت 0.0
انواع داده هایی که برای نمایش در شناور نیمه دقیق توصیه نمی شوند عبارتند از:
- موقعیت داده ها در مختصات جهانی جهانی
- UVs بافت برای موارد استفاده با دقت بالا مانند مختصات عنصر UI در یک صفحه اطلس
دقت در کد سایه زن
زبانهای برنامهنویسی سایهزن OpenGL Shading Language (GLSL) و زبان Shader سطح بالا (HLSL) از مشخصات دقت آرام یا دقت صریح برای انواع عددی پشتیبانی میکنند. دقت آرام به عنوان یک توصیه برای کامپایلر سایه زن در نظر گرفته می شود. دقت صریح لازمه دقت مشخص شده است. دستگاههای Vulkan در اندروید معمولاً از فرمتهای 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;
فرمت عددی داده های گرافیکی و محاسبات شیدر می تواند تاثیر بسزایی در عملکرد بازی شما داشته باشد.
فرمت های بهینه موارد زیر را انجام می دهند:
- افزایش کارایی استفاده از حافظه پنهان GPU
- کاهش مصرف پهنای باند حافظه، صرفه جویی در مصرف انرژی و افزایش عملکرد
- به حداکثر رساندن توان محاسباتی در برنامه های سایه زن
- استفاده از رم پردازنده را در بازی خود به حداقل برسانید
فرمت های ممیز شناور
اکثر محاسبات و داده ها در گرافیک سه بعدی مدرن از اعداد ممیز شناور استفاده می کنند. Vulkan در اندروید از اعداد ممیز شناور استفاده می کند که اندازه آنها 32 یا 16 بیت است. یک عدد ممیز شناور 32 بیتی معمولاً با دقت تک یا دقت کامل شناخته می شود. یک عدد ممیز شناور 16 بیتی، نیم دقت.
Vulkan یک نوع ممیز شناور 64 بیتی تعریف می کند، اما این نوع معمولاً توسط دستگاه های Vulkan در اندروید پشتیبانی نمی شود و استفاده از آن توصیه نمی شود. یک عدد ممیز شناور 64 بیتی معمولا به عنوان دقت مضاعف شناخته می شود.
فرمت های عدد صحیح
از اعداد صحیح علامت دار و بدون علامت نیز برای داده ها و محاسبات استفاده می شود. اندازه عدد صحیح استاندارد 32 بیت است. پشتیبانی از سایر اندازه های بیت به دستگاه بستگی دارد. دستگاه های Vulkan دارای اندروید معمولاً از اعداد صحیح 16 بیتی و 8 بیتی پشتیبانی می کنند. Vulkan یک نوع عدد صحیح 64 بیتی را تعریف می کند، اما این نوع معمولاً توسط دستگاه های Vulkan در اندروید پشتیبانی نمی شود و استفاده از آن توصیه نمی شود.
رفتار نیمه دقیق نابهینه
معماریهای مدرن GPU دو مقدار 16 بیتی را با هم در یک جفت 32 بیتی ترکیب میکنند و دستورالعملهایی را پیادهسازی میکنند که روی این جفت کار میکنند. برای عملکرد بهینه، از استفاده از متغیرهای شناور اسکالر 16 بیتی خودداری کنید. بردار کردن داده ها به بردارهای دو یا چهار عنصری. کامپایلر سایه زن ممکن است بتواند از مقادیر اسکالر در عملیات برداری استفاده کند. با این حال، اگر برای بهینهسازی اسکالرها به کامپایلر تکیه میکنید، خروجی کامپایلر را برای تأیید بردارسازی بررسی کنید.
تبدیل به و از ممیز شناور دقیق 32 بیتی و 16 بیتی هزینه محاسباتی دارد. با به حداقل رساندن تبدیل های دقیق در کد خود، هزینه های اضافی را کاهش دهید.
تفاوت عملکرد بین نسخه های 16 بیتی و 32 بیتی الگوریتم های خود را معیار قرار دهید. نیم دقت همیشه منجر به بهبود عملکرد نمی شود، به خصوص برای محاسبات پیچیده. الگوریتمهایی که از دستورالعملهای ضربافزودن ذوب شده (FMA) روی دادههای بردار استفاده میکنند، کاندیدای خوبی برای بهبود عملکرد با دقت نیمی هستند.
پشتیبانی از فرمت عددی
همه دستگاههای Vulkan در اندروید از اعداد ممیز شناور تک دقیق، ۳۲ بیتی و اعداد صحیح ۳۲ بیتی در محاسبات دادهها و سایهبان پشتیبانی میکنند. پشتیبانی از فرمت های دیگر تضمین نمی شود که در دسترس باشد و در صورت موجود بودن، برای همه موارد استفاده تضمین نمی شود.
Vulkan دارای دو دسته پشتیبانی از فرمت های عددی اختیاری است: حسابی و ذخیره سازی. قبل از استفاده از یک فرمت خاص، مطمئن شوید که یک دستگاه آن را در هر دو دسته پشتیبانی می کند.
پشتیبانی حسابی
یک دستگاه Vulkan باید پشتیبانی حسابی را برای یک قالب عددی اعلام کند تا در برنامه های سایه زن قابل استفاده باشد. دستگاههای Vulkan در اندروید معمولاً از فرمتهای زیر برای محاسبات پشتیبانی میکنند:
- عدد صحیح 32 بیتی (اجباری)
- ممیز شناور 32 بیتی (اجباری)
- عدد صحیح 8 بیتی (اختیاری)
- عدد صحیح 16 بیتی (اختیاری)
- ممیز شناور نیمه دقیق 16 بیتی (اختیاری)
برای تعیین اینکه آیا یک دستگاه Vulkan از اعداد صحیح 16 بیتی برای محاسبات پشتیبانی می کند یا خیر، ویژگی های دستگاه را با فراخوانی تابع vkGetPhysicalDeviceFeatures2() بازیابی کنید و بررسی کنید که آیا فیلد shaderInt16
در ساختار نتیجه VkPhysicalDeviceFeatures2 درست است یا خیر.
برای تعیین اینکه آیا دستگاه Vulkan از شناورهای 16 بیتی یا اعداد صحیح 8 بیتی پشتیبانی می کند، مراحل زیر را انجام دهید:
- بررسی کنید که آیا دستگاه از افزونه VK_KHR_shader_float16_int8 Vulkan پشتیبانی می کند یا خیر. پسوند برای پشتیبانی 16 بیتی شناور و اعداد صحیح 8 بیتی مورد نیاز است.
- اگر
VK_KHR_shader_float16_int8
پشتیبانی میشود، یک نشانگر ساختار VkPhysicalDeviceShaderFloat16Int8Features را به زنجیرهVkPhysicalDeviceFeatures2.pNext
اضافه کنید. - پس از فراخوانی
vkGetPhysicalDeviceFeatures2()
فیلدهایshaderFloat16
وshaderInt8
ساختار نتیجهVkPhysicalDeviceShaderFloat16Int8Features
را بررسی کنید. اگر مقدار فیلدtrue
باشد، قالب برای محاسبات برنامه سایه زن پشتیبانی می شود.
اگرچه در Vulkan 1.1 یا نمایه Android Baseline 2022 الزامی نیست، پشتیبانی از افزونه VK_KHR_shader_float16_int8
در دستگاههای Android بسیار رایج است.
پشتیبانی ذخیره سازی
یک دستگاه Vulkan باید از یک قالب عددی اختیاری برای انواع ذخیره سازی خاص پشتیبانی کند. برنامه افزودنی VK_KHR_16bit_storage پشتیبانی از فرمت های اعداد صحیح 16 بیتی و ممیز شناور 16 بیتی را اعلام می کند. چهار نوع ذخیره سازی توسط افزونه تعریف شده است. یک دستگاه می تواند اعداد 16 بیتی را برای هیچ، برخی یا همه انواع حافظه پشتیبانی کند.
انواع ذخیره سازی عبارتند از:
- ذخیره سازی اشیاء بافر
- اشیاء بافر یکنواخت
- بلوک های ثابت را فشار دهید
- رابط های ورودی و خروجی سایه زن
بیشتر، اما نه همه، دستگاه های Vulkan 1.1 در اندروید از فرمت های 16 بیتی در اشیاء بافر ذخیره سازی پشتیبانی می کنند. پشتیبانی بر اساس مدل GPU را فرض نکنید. ممکن است دستگاههایی با درایورهای قدیمیتر برای یک GPU معین، از اشیاء بافر ذخیرهسازی پشتیبانی نکنند، در حالی که دستگاههایی با درایورهای جدیدتر این کار را انجام میدهند.
پشتیبانی از فرمتهای 16 بیتی در بافرهای یکنواخت، بلوکهای ثابت فشاری و رابطهای ورودی/خروجی سایهزن معمولاً به سازنده GPU وابسته است. در اندروید، یک 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;
}
}
سطح دقیق برای داده ها
یک عدد ممیز شناور با نیم دقت می تواند محدوده کوچکتری از مقادیر را با دقت کمتری نسبت به یک عدد ممیز شناور تک دقیق نشان دهد. نیمه دقیق اغلب یک انتخاب ساده و بدون تلفات ادراکی نسبت به تک دقت است. با این حال، نیم دقت ممکن است در همه موارد استفاده عملی نباشد. برای برخی از انواع داده ها، کاهش دامنه و دقت می تواند منجر به مصنوعات گرافیکی یا ارائه نادرست شود.
انواع داده هایی که کاندیدهای خوبی برای نمایش در ممیز شناور نیمه دقیق هستند عبارتند از:
- داده ها را در مختصات فضای محلی قرار دهید
- UVs بافت برای بافت های کوچکتر با پوشش محدود UV که می تواند در محدوده مختصات -1.0 تا 1.0 محدود شود.
- داده های نرمال، مماس و دو تانژانت
- داده های رنگ راس
- دادههایی با نیازهای دقت پایین با محوریت 0.0
انواع داده هایی که برای نمایش در شناور نیمه دقیق توصیه نمی شوند عبارتند از:
- موقعیت داده ها در مختصات جهانی جهانی
- UVs بافت برای موارد استفاده با دقت بالا مانند مختصات عنصر UI در یک صفحه اطلس
دقت در کد سایه زن
زبانهای برنامهنویسی سایهزن OpenGL Shading Language (GLSL) و زبان Shader سطح بالا (HLSL) از مشخصات دقت آرام یا دقت صریح برای انواع عددی پشتیبانی میکنند. دقت آرام به عنوان یک توصیه برای کامپایلر سایه زن در نظر گرفته می شود. دقت صریح لازمه دقت مشخص شده است. دستگاههای Vulkan در اندروید معمولاً از فرمتهای 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;
فرمت عددی داده های گرافیکی و محاسبات شیدر می تواند تاثیر بسزایی در عملکرد بازی شما داشته باشد.
فرمت های بهینه موارد زیر را انجام می دهند:
- افزایش کارایی استفاده از حافظه پنهان GPU
- کاهش مصرف پهنای باند حافظه، صرفه جویی در مصرف انرژی و افزایش عملکرد
- به حداکثر رساندن توان محاسباتی در برنامه های سایه زن
- استفاده از رم پردازنده را در بازی خود به حداقل برسانید
فرمت های ممیز شناور
اکثر محاسبات و داده ها در گرافیک سه بعدی مدرن از اعداد ممیز شناور استفاده می کنند. Vulkan در اندروید از اعداد ممیز شناور استفاده می کند که اندازه آنها 32 یا 16 بیت است. یک عدد ممیز شناور 32 بیتی معمولاً با دقت تک یا دقت کامل شناخته می شود. یک عدد ممیز شناور 16 بیتی، نیم دقت.
Vulkan یک نوع ممیز شناور 64 بیتی تعریف می کند، اما این نوع معمولاً توسط دستگاه های Vulkan در اندروید پشتیبانی نمی شود و استفاده از آن توصیه نمی شود. یک عدد ممیز شناور 64 بیتی معمولا به عنوان دقت مضاعف شناخته می شود.
فرمت های عدد صحیح
از اعداد صحیح علامت دار و بدون علامت نیز برای داده ها و محاسبات استفاده می شود. اندازه عدد صحیح استاندارد 32 بیت است. پشتیبانی از سایر اندازه های بیت به دستگاه بستگی دارد. دستگاه های Vulkan دارای اندروید معمولاً از اعداد صحیح 16 بیتی و 8 بیتی پشتیبانی می کنند. Vulkan یک نوع عدد صحیح 64 بیتی را تعریف می کند، اما این نوع معمولاً توسط دستگاه های Vulkan در اندروید پشتیبانی نمی شود و استفاده از آن توصیه نمی شود.
رفتار نیمه دقیق نابهینه
معماریهای مدرن GPU دو مقدار 16 بیتی را با هم در یک جفت 32 بیتی ترکیب میکنند و دستورالعملهایی را پیادهسازی میکنند که روی این جفت کار میکنند. برای عملکرد بهینه، از استفاده از متغیرهای شناور اسکالر 16 بیتی خودداری کنید. بردار کردن داده ها به بردارهای دو یا چهار عنصری. کامپایلر سایه زن ممکن است بتواند از مقادیر اسکالر در عملیات برداری استفاده کند. با این حال، اگر برای بهینهسازی اسکالرها به کامپایلر تکیه میکنید، خروجی کامپایلر را برای تأیید بردارسازی بررسی کنید.
تبدیل به و از ممیز شناور دقیق 32 بیتی و 16 بیتی هزینه محاسباتی دارد. با به حداقل رساندن تبدیل های دقیق در کد خود، هزینه های اضافی را کاهش دهید.
تفاوت عملکرد بین نسخه های 16 بیتی و 32 بیتی الگوریتم های خود را معیار قرار دهید. نیم دقت همیشه منجر به بهبود عملکرد نمی شود، به خصوص برای محاسبات پیچیده. الگوریتمهایی که از دستورالعملهای ضربافزودن ذوب شده (FMA) روی دادههای بردار استفاده میکنند، کاندیدای خوبی برای بهبود عملکرد با دقت نیمی هستند.
پشتیبانی از فرمت عددی
همه دستگاههای Vulkan در اندروید از اعداد ممیز شناور تک دقیق، ۳۲ بیتی و اعداد صحیح ۳۲ بیتی در محاسبات دادهها و سایهبان پشتیبانی میکنند. پشتیبانی از فرمت های دیگر تضمین نمی شود که در دسترس باشد و در صورت موجود بودن، برای همه موارد استفاده تضمین نمی شود.
Vulkan دارای دو دسته پشتیبانی از فرمت های عددی اختیاری است: حسابی و ذخیره سازی. قبل از استفاده از یک فرمت خاص، مطمئن شوید که یک دستگاه آن را در هر دو دسته پشتیبانی می کند.
پشتیبانی حسابی
یک دستگاه Vulkan باید پشتیبانی حسابی را برای یک قالب عددی اعلام کند تا در برنامه های سایه زن قابل استفاده باشد. دستگاه های Vulkan در Android معمولاً از قالب های زیر برای حسابی پشتیبانی می کنند:
- عدد صحیح 32 بیتی (اجباری)
- نقطه شناور 32 بیتی (اجباری)
- عدد صحیح 8 بیتی (اختیاری)
- عدد صحیح 16 بیتی (اختیاری)
- نقطه شناور نیمه دقیق 16 بیتی (اختیاری)
برای تعیین اینکه آیا یک دستگاه Vulkan از عدد صحیح 16 بیتی برای حسابی پشتیبانی می کند ، با فراخوانی عملکرد VKGetPhysicalDeviceFeatures2 () و بررسی اینکه آیا زمینه shaderInt16
در VkphysicalDeviceFeatures2 ساختار نتیجه را بازیابی کنید ، ویژگی های دستگاه را بازیابی کنید.
برای تعیین اینکه آیا دستگاه Vulkan از شناورهای 16 بیتی یا عدد صحیح 8 بیتی پشتیبانی می کند ، مراحل زیر را انجام دهید:
- بررسی کنید که آیا دستگاه از VK_KHR_SHADER_FLOAT16_INT8 پسوند Vulkan پشتیبانی می کند. پسوند برای شناور 16 بیتی و پشتیبانی عدد صحیح 8 بیتی مورد نیاز است.
- اگر
VK_KHR_shader_float16_int8
پشتیبانی می شود ، یک VkphysicalDeviceshaderfloat16int8Features Pointer را به یکVkPhysicalDeviceFeatures2.pNext
اضافه کنید. - زمینه های
shaderFloat16
وshaderInt8
از ساختار نتیجهVkPhysicalDeviceShaderFloat16Int8Features
پس از فراخوانیvkGetPhysicalDeviceFeatures2()
را بررسی کنید. اگر مقدار فیلدtrue
باشد ، فرمت برای برنامه Shader برنامه پشتیبانی می شود.
در حالی که یک الزام در Vulkan 1.1 یا مشخصات پایه اندرویدی 2022 نیست ، پشتیبانی از پسوند VK_KHR_shader_float16_int8
در دستگاه های Android بسیار رایج است.
پشتیبانی ذخیره سازی
یک دستگاه Vulkan برای انواع ذخیره سازی خاص باید پشتیبانی از فرمت عددی اختیاری را اعلام کند. گسترش VK_KHR_16BIT_STORAGE پشتیبانی از فرمت های عدد صحیح 16 بیتی و 16 بیتی شناور را اعلام می کند. چهار نوع ذخیره سازی توسط پسوند تعریف شده است. یک دستگاه می تواند شماره های 16 بیتی را برای هیچ ، برخی یا انواع ذخیره سازی پشتیبانی کند.
انواع ذخیره سازی عبارتند از:
- اشیاء بافر ذخیره سازی
- اشیاء بافر یکنواخت
- بلوک های ثابت را فشار دهید
- رابط های ورودی و خروجی سایه بان
بیشتر ، اما نه همه ، دستگاه های Vulkan 1.1 در Android از فرمت های 16 بیتی در اشیاء بافر ذخیره سازی پشتیبانی می کنند. پشتیبانی را بر اساس مدل GPU فرض نکنید. دستگاه هایی با درایورهای قدیمی تر برای یک GPU خاص ممکن است از اشیاء بافر ذخیره سازی پشتیبانی نکنند ، در حالی که دستگاه هایی با درایورهای جدیدتر این کار را انجام می دهند.
پشتیبانی از قالب های 16 بیتی در بافرهای یکنواخت ، بلوک های فشار ثابت و رابط های ورودی/خروجی سایه بان به طور کلی به تولید کننده 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;
}
}
سطح دقیق داده ها
یک شماره نقطه شناور نیمه دقیق می تواند طیف کمتری از مقادیر را با دقت پایین تر از یک شماره نقطه شناور با دقت نشان دهد. نیمه دقیق اغلب یک انتخاب ساده و از نظر ادراکی از ضرر در طی یک دقیق است. با این حال ، نیمه دقیق ممکن است در همه موارد استفاده عملی نباشد. برای برخی از انواع داده ها ، کاهش دامنه و دقت می تواند منجر به مصنوعات گرافیکی یا ارائه نادرست شود.
انواع داده هایی که نامزدهای خوبی برای نمایندگی در نقطه شناور نیمه دقیق هستند عبارتند از:
- داده های موقعیت در مختصات فضای محلی
- UV های بافتی برای بافت های کوچکتر با بسته بندی UV محدود که می تواند محدود به محدوده مختصات -1.0 تا 1.0 باشد
- داده های طبیعی ، مماس و بیتنژنت
- داده های رنگ راس
- داده هایی با نیازهای با دقت کم متمرکز بر 0.0
انواع داده هایی که برای نمایش در شناور نیمه دقیق توصیه نمی شوند شامل موارد زیر هستند:
- داده های موقعیت در مختصات جهانی جهانی
- UV های بافتی برای موارد استفاده با دقت بالا مانند مختصات عنصر UI در یک برگه اطلس
دقت در کد سایه بان
زبان برنامه نویسی سایه بان (GLSL) و زبان سایه دار سطح بالا (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
با استفاده از کلمات کلیدی زیر ، انواع مقیاس ، بردار و ماتریس را در 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;