Daha düşük hassasiyetle optimize edin

Grafik verilerinin sayısal biçiminin ve gölgelendirici hesaplamalarının, oyununuzun performansı üzerinde önemli bir etkisi olabilir.

En uygun biçimler aşağıdakileri sağlar:

  • GPU önbelleği kullanımının verimliliğini artırma
  • Bellek bant genişliği tüketimini azaltın, güç tasarrufu yapın ve performansı artırın
  • Gölgelendirici programlarında hesaplama verimini en üst düzeye çıkarma
  • Oyununuzun CPU RAM kullanımını en aza indirin

Kayan nokta biçimleri

Modern 3D grafiklerdeki hesaplamaların ve verilerin çoğunda kayan noktalı sayılar kullanılır. Android'deki Vulkan, 32 veya 16 bit boyutunda kayan noktalı sayılar kullanır. 32 bitlik kayan noktalı sayılar genellikle tek hassasiyet veya tam hassasiyet, 16 bitlik kayan noktalı sayılar ise yarı hassasiyet olarak adlandırılır.

Vulkan, 64 bit kayan nokta türünü tanımlar ancak bu tür, Android'deki Vulkan cihazları tarafından yaygın olarak desteklenmez ve kullanılması önerilmez. 64 bitlik kayan noktalı sayılar genellikle çift hassasiyet olarak adlandırılır.

Tam sayı biçimleri

Veriler ve hesaplamalar için işaretli ve işaretsiz tam sayılar da kullanılır. Standart tam sayı boyutu 32 bittir. Diğer bit boyutları için destek cihaza bağlıdır. Android çalıştıran Vulkan cihazlar genellikle 16 bit ve 8 bit tam sayıları destekler. Vulkan, 64 bit tam sayı türü tanımlar ancak bu tür, Android'deki Vulkan cihazları tarafından yaygın olarak desteklenmez ve kullanılması önerilmez.

Yetersiz yarı hassasiyetli davranış

Modern GPU mimarileri, iki 16 bitlik değeri 32 bitlik bir çiftte birleştirir ve çift üzerinde çalışan talimatları uygular. En iyi performansı elde etmek için 16 bitlik skaler değişken değişkenleri kullanmaktan kaçının ve verileri iki veya dört öğeli vektörlere dönüştürün. Gölgelendirici derleyicisi, vektör işlemlerinde skaler değerler kullanabilir. Ancak skalerleri optimize etmek için derleyiciye güveniyorsanız vektörlemeyi doğrulamak için derleyici çıkışını inceleyin.

32 bit ve 16 bit hassasiyetli kayan nokta arasında dönüştürme işleminin hesaplama maliyeti vardır. Kodunuzda hassas dönüşümleri en aza indirerek ek maliyeti azaltın.

Algoritmalarınızın 16 bit ve 32 bit sürümleri arasındaki performans farklılıklarını karşılaştırın. Yarı hassasiyet, özellikle karmaşık hesaplamalar için her zaman performans artışı sağlamaz. Vektörleştirilmiş verilerde birleştirilmiş çarpma ve toplama (FMA) talimatlarını yoğun şekilde kullanan algoritmalar, yarı kesinlikte daha iyi performans için iyi adaylardır.

Sayısal biçim desteği

Android'deki tüm Vulkan cihazları, verilerde ve gölgelendirici hesaplamalarında tek duyarlıklı, 32 bit kayan nokta numaralarını ve 32 bit tamsayıları destekler. Diğer biçimler için destek sunulacağı garanti edilmez ve sunulsa bile tüm kullanım alanları için destek sunulacağı garanti edilmez.

Vulkan, isteğe bağlı sayısal biçimler için iki destek kategorisine sahiptir: aritmetik ve depolama. Belirli bir biçimi kullanmadan önce cihazın, biçimi her iki kategoride de desteklediğinden emin olun.

Aritmetik desteği

Gölgelendirici programlarında kullanılabilmesi için bir Vulkan cihazının sayısal bir biçime ilişkin aritmetik destek bildirmesi gerekir. Android'deki Vulkan cihazlar, aritmetik için genellikle aşağıdaki biçimleri destekler:

  • 32 bit tam sayı (zorunlu)
  • 32 bit kayan nokta (zorunlu)
  • 8 bit tam sayı (isteğe bağlı)
  • 16 bit tam sayı (isteğe bağlı)
  • 16 bit yarı hassas kayan nokta (isteğe bağlı)

Bir Vulkan cihazının aritmetik için 16 bit tam sayıları destekleyip desteklemediğini belirlemek amacıyla vkGetPhysicalDeviceFeatures2() işlevini çağırarak cihazın özelliklerini alın ve VkPhysicalDeviceFeatures2 sonuç yapısındaki shaderInt16 alanının doğru olup olmadığını kontrol edin.

Bir Vulkan cihazının 16 bit kayan öğeleri mi yoksa 8 bit tam sayıları mı desteklediğini belirlemek için aşağıdaki adımları uygulayın:

  1. Cihazın VK_KHR_shader_float16_int8 Vulkan uzantısını destekleyip desteklemediğini kontrol edin. Bu uzantı, 16 bit kayan nokta ve 8 bit tam sayı desteği için gereklidir.
  2. VK_KHR_shader_float16_int8 destekleniyorsa VkPhysicalDeviceFeatures2.pNext zincirine bir VkPhysicalDeviceShaderFloat16Int8Features yapı işaretçisi ekleyin.
  3. vkGetPhysicalDeviceFeatures2() çağrıldıktan sonra VkPhysicalDeviceShaderFloat16Int8Features sonuç yapısının shaderFloat16 ve shaderInt8 alanlarını kontrol edin. Alan değeri true ise biçim, gölgelendirici programı aritmetiği için desteklenir.

Vulkan 1.1 veya 2022 Android temel profili için zorunlu olmasa da VK_KHR_shader_float16_int8 uzantısı Android cihazlarda yaygın olarak desteklenir.

Depolama alanı desteği

Vulkan cihazı, belirli depolama türleri için isteğe bağlı bir sayısal biçim desteğini beyan etmelidir. VK_KHR_16bit_storage uzantısı, 16 bit tam sayı ve 16 bit kayan nokta biçimlerini desteklediğini belirtir. Uzantı dört depolama alanı türü tanımlar. Cihaz, hiçbir depolama alanı, bir kısmı veya tüm depolama türleri için 16 bitlik numaraları destekleyebilir.

Depolama alanı türleri şunlardır:

  • Depolama alanı tampon nesneleri
  • Tekdüzen arabellek nesneleri
  • İtme sabit blokları
  • Gölgelendirici giriş ve çıkış arayüzleri

Android'deki Vulkan 1.1 cihazların tümü olmasa da çoğu, depolama alanı arabellek nesnelerinde 16 bit biçimleri destekler. GPU modeline bağlı olarak destek sağlanacağını varsaymayın. Belirli bir GPU için eski sürücülere sahip cihazlar depolama alanı arabellek nesnelerini desteklemeyebilir. Yeni sürücülere sahip cihazlar ise bu nesneleri destekler.

Tek tip arabelleklerde, sabit itme bloklarında ve gölgelendirici giriş/çıkış arayüzlerinde 16 bitlik biçimler için destek genellikle GPU üreticisine bağlıdır. Android'de bir GPU genellikle bu türlerin üçünü de veya hiçbirini desteklemez.

Vulkan aritmetiği ve depolama biçimi desteğini test eden örnek bir işlev:

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;
  }
}

Veriler için hassasiyet düzeyi

Yarım hassasiyetli kayan nokta sayısı, tek hassasiyetli kayan nokta sayısından daha düşük bir hassasiyetle daha küçük bir değer aralığını temsil edebilir. Yarım hassasiyet, genellikle tek hassasiyete kıyasla basit ve duyusal olarak kayıpsız bir seçimdir. Ancak yarı hassasiyet, tüm kullanım alanlarında pratik olmayabilir. Bazı veri türlerinde, azaltılmış aralık ve hassasiyet grafik kusurlarına veya yanlış oluşturmaya neden olabilir.

Yarı hassas kayan noktada temsil edilmeye iyi aday olan veri türleri şunlardır:

  • Yerel uzay koordinatlarında konum verileri
  • -1,0 ile 1,0 koordinat aralığına sınırlanabilen sınırlı UV sarmalaması olan daha küçük dokular için doku UV'leri
  • Normal, teğet ve çift teğet verileri
  • Köşe noktası renk verileri
  • 0,0 değerine odaklanan düşük hassasiyet koşullarına sahip veriler

Yarım hassasiyetli kayan noktayla temsil edilmesi önerilmeyen veri türleri şunlardır:

  • Dünya koordinatlarında konum verileri
  • Bir atlas sayfasında kullanıcı arayüzü öğesi koordinatları gibi yüksek hassasiyetli kullanım alanları için doku UV'leri

Gölgelendirici kodunda hassasiyet

OpenGL Gölgelendirme Dili (GLSL) ve Yüksek Seviye Gölgelendirme Dili (HLSL) gölgelendirici programlama dilleri, sayısal türler için gevşek hassasiyet veya açık hassasiyet belirtmeyi destekler. Esnek kesinlik, gölgelendirici derleyici için bir öneri olarak değerlendirilir. Açık hassasiyet, belirtilen hassasiyetin gereklidir. Android'deki Vulkan cihazlar, yüksek hassasiyetle önerildiğinde genellikle 16 bit biçimleri kullanır. Diğer Vulkan cihazları, özellikle de 16 bit biçimleri için destek vermeyen grafik donanımı kullanan masaüstü bilgisayarlardaki rahat hassasiyeti görmezden gelebilir ve 32 bit biçimlerini kullanmaya devam edebilir.

GLSL'de depolama uzantıları

Depolama ve tek tip arabellek yapılarında 16 bit veya 8 bit sayısal biçimlerin desteklenmesini sağlamak için uygun GLSL uzantıları tanımlanmalıdır. İlgili uzantı beyanları şunlardır:

// 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

Bu uzantılar GLSL'ye özeldir ve HLSL'de eşdeğeri yoktur.

GLSL'de gevşek hassasiyet

Tek hassasiyetli kayan nokta önermek için kayan nokta türünden önce highp seçicisini, yarı hassasiyetli kayan nokta önermek için ise mediump seçicisini kullanın. Vulkan için GLSL derleyicileri, eski lowp belirteci mediump olarak yorumlar. Gevşek hassasiyete örnek olarak şunlar verilebilir:

mediump vec4 my_vector; // Suggest 16-bit half precision
highp mat4 my_matrix;   // Suggest 32-bit single precision

GLSL'de açık hassasiyet

16 bit kayan nokta türlerinin kullanımını etkinleştirmek için GLSL kodunuza GL_EXT_shader_explicit_arithmetic_types_float16 uzantısını ekleyin:

#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require

Aşağıdaki anahtar kelimeleri kullanarak GLSL'de 16 bit kayan noktalı skaler, vektör ve matris türlerini tanımlayın:

float16_t   f16vec2     f16vec3    f16vec4
f16mat2     f16mat3     f16mat4
f16mat2x2   f16mat2x3   f16mat2x4
f16mat3x2   f16mat3x3   f16mat3x4
f16mat4x2   f16mat4x3   f16mat4x4

Aşağıdaki anahtar kelimeleri kullanarak GLSL'de 16 bitlik tamsayı skaler ve vektör türlerini tanımlayın:

int16_t     i16vec2     i16vec3    i16vec4
uint16_t    u16vec2     u16vec3    u16vec4

HLSL'de gevşek hassasiyet

HLSL, esnek hassasiyet yerine minimum hassasiyet terimini kullanır. Minimum hassasiyetli anahtar kelimeler, minimum hassasiyeti belirtir ancak hedef donanım için daha yüksek hassasiyet daha iyi bir seçim olursa derleyici daha yüksek bir hassasiyet seçebilir. min16float anahtar kelimesi, minimum hassasiyete sahip 16 bitlik bir kayan nokta değeri belirtir. Minimum hassasiyete sahip imzalı ve imzasız 16 bit tam sayılar sırasıyla min16int ve min16uint anahtar kelimeleriyle belirtilir. Minimum hassasiyet bildirimleriyle ilgili diğer örnekler şunlardır:

// Four element vector and four-by-four matrix types
min16float4 my_vector4;
min16float4x4 my_matrix4x4;

HLSL'de açık hassasiyet

Yarı duyarlıklı kayan nokta, half veya float16_t anahtar kelimeleriyle belirtilir. İmzalı ve imzasız 16 bit tam sayılar sırasıyla int16_t ve uint16_t anahtar kelimeleriyle belirtilir. Belirli hassasiyet beyanlarına örnek olarak aşağıdakiler verilebilir:

// Four element vector and four-by-four matrix types
half4 my_vector4;
half4x4 my_matrix4x4;