รูปแบบตัวเลขของข้อมูลกราฟิกและการคำนวณเฉดสีอาจส่งผลต่อประสิทธิภาพของเกมอย่างมาก
รูปแบบที่เหมาะสมที่สุดจะทำสิ่งต่อไปนี้
- เพิ่มประสิทธิภาพการใช้แคช GPU
- ลดการใช้แบนด์วิดท์ของหน่วยความจำ ประหยัดพลังงาน และเพิ่มประสิทธิภาพ
- เพิ่มอัตราการประมวลผลสูงสุดในโปรแกรมเฉดสี
- ลดการใช้ RAM ของ CPU ในเกม
รูปแบบจุดลอยตัว
การคำนวณและข้อมูลส่วนใหญ่ในกราฟิก 3 มิติสมัยใหม่ใช้ตัวเลขจุดลอยตัว Vulkan ใน Android ใช้ตัวเลขจุดลอยตัวที่มีขนาด 32 หรือ 16 บิต โดยทั่วไปแล้ว ตัวเลขจุดลอยตัวแบบ 32 บิตจะเรียกว่าความแม่นยำเดี่ยวหรือความแม่นยำเต็ม ส่วนตัวเลขจุดลอยตัวแบบ 16 บิตจะเรียกว่าความแม่นยำครึ่งเดียว
Vulkan กำหนดประเภทจุดลอยตัว 64 บิต แต่อุปกรณ์ Vulkan ใน Android ไม่รองรับประเภทนี้โดยทั่วไป และเราไม่แนะนำให้ใช้ ตัวเลขจุดลอยตัวแบบ 64 บิตมักเรียกว่าความแม่นยำแบบ Double
รูปแบบจำนวนเต็ม
นอกจากนี้ยังใช้จำนวนเต็มแบบมีเครื่องหมายและไม่มีเครื่องหมายสำหรับข้อมูลและการคำนวณด้วย ขนาดจำนวนเต็มมาตรฐานคือ 32 บิต การรองรับบิตขนาดอื่นๆ จะขึ้นอยู่กับอุปกรณ์ อุปกรณ์ Vulkan ที่ใช้ Android มักจะรองรับจำนวนเต็ม 16 บิตและ 8 บิต Vulkan กำหนดประเภทจำนวนเต็ม 64 บิต แต่อุปกรณ์ Vulkan ใน Android ไม่ค่อยรองรับประเภทนี้ และเราไม่แนะนำให้ใช้
ลักษณะการทำงานที่มีความแม่นยำครึ่งหนึ่งซึ่งไม่เหมาะสม
สถาปัตยกรรม GPU สมัยใหม่จะรวมค่า 16 บิต 2 ค่าเข้าด้วยกันเป็นคู่ 32 บิต และใช้คำสั่งที่ทำงานกับคู่นั้น เพื่อประสิทธิภาพสูงสุด ให้หลีกเลี่ยงการใช้ตัวแปรสเกลาร์แบบโฟลต 16 บิต และจัดเวกเตอร์ข้อมูลเป็นเวกเตอร์ 2 หรือ 4 องค์ประกอบ คอมไพเลอร์ตัวปรับเฉดสีอาจใช้ค่าสเกลาร์ในการดำเนินการเวกเตอร์ได้ อย่างไรก็ตาม หากคุณใช้คอมไพเลอร์เพื่อเพิ่มประสิทธิภาพสเกลาร์ ให้ตรวจสอบเอาต์พุตของคอมไพเลอร์เพื่อยืนยันการทำเวกเตอร์
การแปลงไปมาระหว่างจุดลอยตัวความแม่นยำ 32 บิตและ 16 บิตมีต้นทุนการประมวลผล ลดค่าใช้จ่ายในการดำเนินการโดยลดการแปลงความแม่นยำในโค้ด
เปรียบเทียบความแตกต่างด้านประสิทธิภาพระหว่างอัลกอริทึมเวอร์ชัน 16 บิตและ 32 บิต ความแม่นยำครึ่งหนึ่งไม่ได้ส่งผลให้ประสิทธิภาพดีขึ้นเสมอไป โดยเฉพาะอย่างยิ่งสำหรับการคำนวณที่ซับซ้อน อัลกอริทึมที่ใช้คำสั่ง Fused Multiply-Add (FMA) อย่างหนักกับข้อมูลแบบเวกเตอร์เป็นตัวเลือกที่ดีสำหรับการปรับปรุงประสิทธิภาพที่ความแม่นยำครึ่งหนึ่ง
การรองรับรูปแบบตัวเลข
อุปกรณ์ Vulkan ทั้งหมดใน Android รองรับตัวเลขจุดลอยตัวแบบความแม่นยำเดี่ยว 32 บิตและตัวเลขจำนวนเต็ม 32 บิตในการคำนวณข้อมูลและเฉดสี เราไม่รับประกันว่ารูปแบบอื่นๆ จะพร้อมใช้งาน และหากพร้อมใช้งาน ก็ไม่รับประกันว่าจะใช้ได้กับ Use Case ทั้งหมด
Vulkan มีการรองรับรูปแบบตัวเลขที่ไม่บังคับ 2 หมวดหมู่ ได้แก่ เลขคณิตและพื้นที่เก็บข้อมูล ก่อนใช้รูปแบบที่เฉพาะเจาะจง โปรดตรวจสอบว่าอุปกรณ์รองรับรูปแบบดังกล่าวทั้ง 2 หมวดหมู่
การสนับสนุนด้านเลขคณิต
อุปกรณ์ Vulkan ต้องประกาศการรองรับเลขคณิตสำหรับรูปแบบตัวเลขเพื่อให้ใช้ในโปรแกรม Shader ได้ อุปกรณ์ 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ให้เพิ่มตัวชี้โครงสร้าง VkPhysicalDeviceShaderFloat16Int8Features ต่อท้ายเชนVkPhysicalDeviceFeatures2.pNext - ตรวจสอบช่อง
shaderFloat16และshaderInt8ของโครงสร้างผลลัพธ์VkPhysicalDeviceShaderFloat16Int8Featuresหลังจากเรียกใช้vkGetPhysicalDeviceFeatures2()หากค่าของช่องเป็นtrueแสดงว่ารูปแบบนี้รองรับการคำนวณทางคณิตศาสตร์ของโปรแกรมเฉดสี
แม้ว่าจะไม่ได้เป็นข้อกำหนดใน Vulkan 1.1 หรือโปรไฟล์พื้นฐานของ Android ปี 2022 แต่การรองรับส่วนขยาย VK_KHR_shader_float16_int8 ก็พบได้ทั่วไปในอุปกรณ์ Android
การสนับสนุนด้านพื้นที่เก็บข้อมูล
อุปกรณ์ Vulkan ต้องประกาศการรองรับรูปแบบตัวเลขที่ไม่บังคับสำหรับพื้นที่เก็บข้อมูลบางประเภท ส่วนขยาย VK_KHR_16bit_storage ประกาศการรองรับรูปแบบจำนวนเต็ม 16 บิตและจุดลอยตัว 16 บิต ส่วนขยายจะกำหนดพื้นที่เก็บข้อมูล 4 ประเภท อุปกรณ์สามารถรองรับตัวเลข 16 บิตสำหรับพื้นที่เก็บข้อมูลบางประเภทหรือทุกประเภท หรือไม่รองรับเลยก็ได้
ประเภทพื้นที่เก็บข้อมูลมีดังนี้
- ออบเจ็กต์บัฟเฟอร์พื้นที่เก็บข้อมูล
- ออบเจ็กต์บัฟเฟอร์แบบรวม
- พุชบล็อกค่าคงที่
- อินเทอร์เฟซอินพุตและเอาต์พุตของเฉดสี
อุปกรณ์ Vulkan 1.1 ส่วนใหญ่ (แต่ไม่ใช่ทั้งหมด) ใน Android รองรับรูปแบบ 16 บิตในออบเจ็กต์บัฟเฟอร์พื้นที่เก็บข้อมูล อย่าคาดเดาการรองรับโดยอิงตามรุ่น GPU อุปกรณ์ที่มีไดรเวอร์รุ่นเก่าสำหรับ GPU หนึ่งๆ อาจไม่รองรับออบเจ็กต์บัฟเฟอร์พื้นที่เก็บข้อมูล แต่อุปกรณ์ที่มีไดรเวอร์รุ่นใหม่กว่าจะรองรับ
การรองรับรูปแบบ 16 บิตในบัฟเฟอร์แบบ Uniform, บล็อกค่าคงที่แบบพุช และอินเทอร์เฟซอินพุต/เอาต์พุตของตัวปรับเฉดสีโดยทั่วไปจะขึ้นอยู่กับผู้ผลิต GPU ใน Android โดยทั่วไปแล้ว GPU จะรองรับทั้ง 3 ประเภทนี้หรือไม่รองรับเลย
ตัวอย่างฟังก์ชันที่ทดสอบการรองรับรูปแบบเลขคณิตและพื้นที่เก็บข้อมูลของ 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
- ข้อมูล Normal, Tangent และ Bitangent
- ข้อมูลสีของจุดยอด
- ข้อมูลที่มีข้อกำหนดความแม่นยำต่ำซึ่งมีศูนย์กลางอยู่ที่ 0.0
ประเภทข้อมูลที่ไม่แนะนำให้แสดงในรูปแบบ Half-Precision Float ได้แก่
- ข้อมูลตำแหน่งในพิกัดโลก
- UV ของพื้นผิวสำหรับกรณีการใช้งานที่มีความแม่นยำสูง เช่น พิกัดองค์ประกอบ UI ในแผ่น Atlas
ความแม่นยำในโค้ดเฉดสี
ภาษาโปรแกรมเฉดเดอร์ 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 ใช้คำว่าความแม่นยำขั้นต่ำแทนความแม่นยำแบบผ่อนปรน คีย์เวิร์ดประเภทความแม่นยำขั้นต่ำจะระบุความแม่นยำขั้นต่ำ แต่คอมไพเลอร์อาจแทนที่ด้วยความแม่นยำที่สูงขึ้นหากความแม่นยำที่สูงขึ้นเป็นตัวเลือกที่ดีกว่าสำหรับฮาร์ดแวร์เป้าหมาย คีย์เวิร์ด min16float จะระบุค่าทศนิยม 16 บิตที่มีความแม่นยำน้อยที่สุด จำนวนเต็ม 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;