รูปแบบตัวเลขของข้อมูลกราฟิกและการคำนวณเชดเดอร์อาจส่งผลต่อประสิทธิภาพของเกมได้อย่างมาก
รูปแบบที่เหมาะสมจะมีลักษณะดังนี้
- เพิ่มประสิทธิภาพการใช้แคช GPU
- ลดการใช้แบนด์วิดท์ของหน่วยความจำ ประหยัดพลังงาน และเพิ่มประสิทธิภาพ
- เพิ่มประสิทธิภาพการประมวลผลสูงสุดในโปรแกรม Shader
- ลดการใช้ RAM ของ CPU ในเกม
รูปแบบจุดลอยตัว
การคํานวณและข้อมูลส่วนใหญ่ในกราฟิก 3 มิติสมัยใหม่ใช้ตัวเลขทศนิยม Vulkan ใน Android ใช้เลขทศนิยมขนาด 32 หรือ 16 บิต จํานวนทศนิยม 32 บิตมักเรียกว่าความแม่นยําแบบเดี่ยวหรือความแม่นยําแบบเต็ม จํานวนทศนิยม 16 บิตเรียกว่าความแม่นยําแบบครึ่ง
Vulkan กำหนดประเภทเลขทศนิยม 64 บิต แต่อุปกรณ์ Vulkan ใน Android มักไม่รองรับประเภทนี้ และเราไม่แนะนำให้ใช้ จํานวนทศนิยม 64 บิตมักเรียกว่า "ความแม่นยําแบบคู่"
รูปแบบจำนวนเต็ม
ระบบยังใช้จำนวนเต็มที่มีเครื่องหมายและไม่มีเครื่องหมายสำหรับข้อมูลและการคำนวณด้วย ขนาดจำนวนเต็มมาตรฐานคือ 32 บิต การสนับสนุนขนาดบิตอื่นๆ จะขึ้นอยู่กับอุปกรณ์ อุปกรณ์ Vulkan ที่ทำงานบน Android มักรองรับจำนวนเต็ม 16 บิตและ 8 บิต Vulkan กำหนดประเภทจำนวนเต็ม 64 บิต แต่อุปกรณ์ Vulkan ใน Android มักไม่รองรับประเภทนี้ และเราไม่แนะนำให้ใช้
ลักษณะการทํางานแบบครึ่งความแม่นยำที่ไม่เหมาะสม
สถาปัตยกรรม GPU สมัยใหม่จะรวมค่า 16 บิต 2 ค่าเข้าด้วยกันเป็นคู่ 32 บิต และติดตั้งใช้งานคำสั่งที่ทำงานกับคู่ดังกล่าว เพื่อประสิทธิภาพที่ดีที่สุด ให้หลีกเลี่ยงการใช้ตัวแปรประเภทจำนวนจริง 16 บิตที่เป็นสเกลาร์ และเปลี่ยนข้อมูลเป็นเวกเตอร์แบบ 2 หรือ 4 องค์ประกอบ คอมไพเลอร์ Shader อาจใช้ค่าสเกลาร์ในการดำเนินการกับเวกเตอร์ได้ อย่างไรก็ตาม หากคุณใช้คอมไพเลอร์เพื่อเพิ่มประสิทธิภาพสเกลาร์ ให้ตรวจสอบเอาต์พุตของคอมไพเลอร์เพื่อยืนยันการจัดเวกเตอร์
การแปลงเป็นและจากจุดลอยตัวแบบ 32 บิตและ 16 บิตที่มีความแม่นยำจะเพิ่มต้นทุนการประมวลผล ลดค่าใช้จ่ายในการดำเนินการโดยลด Conversion ที่แม่นยำในโค้ด
เปรียบเทียบความแตกต่างของประสิทธิภาพระหว่างอัลกอริทึมเวอร์ชัน 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 บิตหรือไม่ ให้ทำตามขั้นตอนต่อไปนี้
- ตรวจสอบว่าอุปกรณ์รองรับส่วนขยาย VK_KHR_shader_float16_int8 ของ Vulkan หรือไม่ จำเป็นต้องใช้ส่วนขยายนี้เพื่อรองรับจำนวนทศนิยม 16 บิตและจำนวนเต็ม 8 บิต
- หากรองรับ
VK_KHR_shader_float16_int8
ให้เพิ่มเคอร์เซอร์โครงสร้าง VkPhysicalDeviceShaderFloat16Int8Features ต่อท้ายเชนVkPhysicalDeviceFeatures2.pNext
- ตรวจสอบช่อง
shaderFloat16
และshaderInt8
ของโครงสร้างผลการค้นหาVkPhysicalDeviceShaderFloat16Int8Features
หลังจากเรียกใช้vkGetPhysicalDeviceFeatures2()
หากค่าในช่องคือtrue
แสดงว่ารูปแบบรองรับการดำเนินการทางคณิตศาสตร์ของโปรแกรม Shader
แม้ว่าจะไม่ใช่ข้อกำหนดใน Vulkan 1.1 หรือโปรไฟล์พื้นฐานของ Android ปี 2022 แต่การรองรับส่วนขยาย VK_KHR_shader_float16_int8
นั้นพบได้ทั่วไปในอุปกรณ์ Android
การรองรับพื้นที่เก็บข้อมูล
อุปกรณ์ Vulkan ต้องประกาศรองรับรูปแบบตัวเลขที่ไม่บังคับสำหรับประเภทพื้นที่เก็บข้อมูลหนึ่งๆ ส่วนขยาย VK_KHR_16bit_storage ประกาศการรองรับรูปแบบจำนวนเต็ม 16 บิตและรูปแบบทศนิยม 16 บิต ส่วนขยายจะกําหนดพื้นที่เก็บข้อมูล 4 ประเภท อุปกรณ์อาจรองรับตัวเลข 16 บิตสำหรับพื้นที่เก็บข้อมูลบางประเภท บางส่วน หรือทั้งหมด
ประเภทพื้นที่เก็บข้อมูลมีดังนี้
- ออบเจ็กต์บัฟเฟอร์พื้นที่เก็บข้อมูล
- ออบเจ็กต์บัฟเฟอร์แบบรวม
- บล็อกค่าคงที่ของ Push
- อินเทอร์เฟซอินพุตและเอาต์พุตของ Shader
อุปกรณ์ Vulkan 1.1 ส่วนใหญ่ใน Android รองรับรูปแบบ 16 บิตในออบเจ็กต์บัฟเฟอร์พื้นที่เก็บข้อมูล แต่ไม่ใช่ทั้งหมด อย่าคิดว่ารองรับโดยอิงตามรุ่น GPU อุปกรณ์ที่ใช้ไดรเวอร์เก่าสำหรับ GPU บางรุ่นอาจไม่รองรับออบเจ็กต์บัฟเฟอร์พื้นที่เก็บข้อมูล ขณะที่อุปกรณ์ที่ใช้ไดรเวอร์รุ่นใหม่จะรองรับ
โดยทั่วไปแล้ว การสนับสนุนรูปแบบ 16 บิตในบัฟเฟอร์แบบรวม บล็อกค่าคงที่แบบพุช และอินเทอร์เฟซอินพุต/เอาต์พุตของโปรแกรมเปลี่ยนรูปแบบจะขึ้นอยู่กับผู้ผลิต 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
- ข้อมูลปกติ สัมผัส และสัมผัสคู่
- ข้อมูลสีของจุดยอด
- ข้อมูลที่มีข้อกำหนดความแม่นยำต่ำซึ่งอยู่กึ่งกลางที่ 0.0
ประเภทข้อมูลที่ไม่แนะนําให้แสดงเป็นเลขทศนิยมครึ่งความแม่นยำมีดังนี้
- ข้อมูลตำแหน่งในพิกัดโลก
- UV ของพื้นผิวสำหรับกรณีการใช้งานที่มีความแม่นยำสูง เช่น พิกัดองค์ประกอบ UI ในชีตแผนที่
ความแม่นยำในโค้ด Shader
ภาษาโปรแกรม OpenGL Shading Language (GLSL) และ High-level Shader Language (HLSL) รองรับข้อกำหนดความแม่นยำแบบผ่อนปรนหรือความแม่นยำแบบชัดเจนสำหรับประเภทตัวเลข ระบบจะถือว่าความแม่นยำแบบผ่อนปรนเป็นคำแนะนำสำหรับคอมไพเลอร์ Shader ความแม่นยำที่ชัดเจนเป็นข้อกำหนดของความแม่นยำที่ระบุ โดยทั่วไปแล้ว อุปกรณ์ 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;