Daha düşük hassasiyetle optimize edin

Grafik verilerinin sayısal biçimi ve gölgelendirici hesaplamaları, oyununuzun performansı üzerinde önemli bir etkiye sahip olabilir.

Optimum biçimler aşağıdakileri yapar:

  • GPU önbellek kullanımının verimliliğini artırma
  • Bellek bant genişliği tüketimini azaltarak güçten tasarruf edin ve performansı artırın
  • Gölgelendirici programlarında sayısal işleme hızını en üst düzeye çıkarma
  • Oyununuzun CPU RAM kullanımını en aza indirme

Kayan nokta biçimleri

Modern 3D grafiklerdeki hesaplamaların ve verilerin çoğunda kayan nokta sayıları kullanılır. Android'de Vulkan, 32 veya 16 bit boyutunda kayan nokta sayıları kullanır. 32 bit kayan nokta sayısına genellikle tek kesinlik veya tam hassasiyet; 16 bitlik kayan nokta sayısı ve yarı kesinlikli denir.

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

Tam sayı biçimleri

İşaretli ve imzasız tamsayılar, veri ve hesaplamalar için de 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 bitlik bir tam sayı türünü tanımlar ancak bu tür, Android'de Vulkan cihazları tarafından yaygın olarak desteklenmez ve kullanılması önerilmez.

İdeal olmayan yarı kesinlik davranışı

Modern GPU mimarileri, iki 16 bit değeri bir 32 bit çiftinde birleştirir ve çift üzerinde çalışan talimatları uygular. Optimum performans için skaler 16 bit kayan değişken kullanmaktan kaçının; verileri iki veya dört öğeli vektörlere ayırın. Gölgelendirici derleyici, vektör işlemlerinde skalar değerler kullanabilir. Bununla birlikte, skalerleri optimize etmek için derleyiciden yararlanıyorsanız vektörleştirmeyi doğrulamak için derleyici çıkışını inceleyin.

32 bit ve 16 bit hassasiyetli kayan noktaya/noktadan dönüştürme işleminin hesaplama maliyeti vardır. Kodunuzdaki hassasiyet dönüşümlerini en aza indirerek ek yükü azaltın.

Algoritmalarınızın 16 ve 32 bit sürümleri arasındaki performans farklarını karşılaştırın. Yarı hassasiyet, özellikle karmaşık hesaplamalarda her zaman performans artışı sağlamaz. Vektörleştirilmiş verilerde çok kaynaklı çok eklemeli (FMA) talimatların yoğun bir şekilde kullanıldığı algoritmalar, yarı hassasiyette iyileştirilmiş performans için iyi adaylardır.

Sayısal biçim desteği

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

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

Aritmetik destek

Bir Vulkan cihazının gölgelendirici programlarında kullanılabilmesi için sayısal biçime ilişkin aritmetik destek beyan etmesi 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ı duyarlıklı 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 ve VkPhysicalDeviceFeatures2 sonuç yapısındaki shaderInt16 alanının doğru olup olmadığını kontrol ederek cihazın özelliklerini alın.

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. 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 VkFizikselDeviceShaderFloat16Int8Features yapısı işaretçisi ekleyin.
  3. vkGetPhysicalDeviceFeatures2() çağrısından 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 Baseline profilinde bir gereklilik bulunmasa da VK_KHR_shader_float16_int8 uzantısı, Android cihazlarda çok yaygın bir şekilde desteklenir.

Depolama alanı desteği

Vulkan cihazları, belirli depolama türleri için isteğe bağlı sayısal biçimi desteklediğini beyan etmelidir. VK_KHR_16bit_storage uzantısı, 16 bit tam sayı ve 16 bit kayan nokta biçimlerini desteklediğini beyan eder. Uzantı tarafından dört depolama alanı türü tanımlanır. Bir cihaz; depolama alanı türlerinin hiçbiri, bir kısmı veya tüm depolama türleri için 16 bitlik numaraları destekleyebilir.

Depolama türleri şunlardır:

  • Depolama arabellek nesneleri
  • Tek tip arabellek nesneleri
  • Sabit blokları itin
  • Gölgelendirici giriş ve çıkış arayüzleri

Android'deki Vulkan 1.1 cihazlarının tümü olmasa da çoğu, depolama arabellek nesnelerinde 16 bit biçimleri destekler. GPU modeline dayalı destek olduğunu varsaymayın. Belirli bir GPU için eski sürücülere sahip cihazlar depolama arabellek nesnelerini desteklemeyebilirken, yeni sürücülere sahip cihazlar destekler.

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

Vulkan aritmetiği ve depolama biçimi desteğini test eden bir örnek 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 doğruluk düzeyi

Yarı kesinlikli kayan nokta sayısı, tek duyarlıklı bir kayan nokta sayısına göre daha düşük bir hassasiyetle daha küçük bir değer aralığını temsil edebilir. Yarı kesinlik, tek duyarlılığa kıyasla genellikle basit ve kayıpsız bir seçimdir. Bununla birlikte, yarı kesinlik tüm kullanım alanlarında pratik olmayabilir. Bazı veri türlerinde, düşük aralık ve hassasiyet nedeniyle grafik yapılar veya yanlış oluşturma görülebilir.

Yarı duyarlı kayan nokta için iyi birer aday olan veri türleri şunlardır:

  • Verileri yerel alan koordinatlarına göre konumlandırın
  • -1,0 ila 1,0 koordinat aralığıyla sınırlandırılabilen sınırlı UV sarmalama içeren daha küçük dokular için doku UV'leri
  • Normal, teğet ve bitangent veriler
  • Köşe renk verileri
  • 0,0 merkezli, düşük hassasiyet gereksinimleri olan veriler

Yarı duyarlı kayan reklamda gösterim için önerilmeyen veri türleri şunları içerir:

  • Verileri küresel dünya koordinatlarına göre konumlandırın
  • Atlas sayfasındaki kullanıcı arayüzü öğesi koordinatları gibi yüksek hassasiyetli kullanım alanları için doku UV'leri

Gölgelendirici kodunda hassasiyet

OpenGL Gölgeleme Dili (GLSL) ve Üst Düzey Gölgelendirici Dili (HLSL) gölgelendirici programlama dilleri, sayısal türler için rahat kesinlik veya açık kesinlik spesifikasyonunu destekler. Esnek hassasiyet, gölgelendirici derleyici için bir öneri olarak değerlendirilir. Açık hassasiyet, belirtilen hassasiyet için bir gereksinimdir. Esnek hassasiyetle önerildiğinde Android'deki Vulkan cihazları genellikle 16 bit biçimleri kullanır. Diğer Vulkan cihazları, özellikle 16 bit biçimleri desteklemeyen grafik donanımı kullanan masaüstü bilgisayarlarda, esnek hassasiyeti yok sayabilir ve yine de 32 bit biçimlerini kullanabilir.

GLSL'de depolama uzantıları

Depolama ve tek tip arabellek yapılarında 16 bit veya 8 bit sayısal biçimlerin desteklenmesi için uygun GLSL uzantıları tanımlanmalıdır. İlgili uzantı bildirimleri ş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 bir eşdeğeri yoktur.

GLSL'de gevşek hassasiyet

Tek duyarlıklı kayan nokta önermek için kayan nokta türünden önce highp niteleyicisini ve yarı duyarlılıklı bir hareketli değer için mediump niteleyicisini kullanın. Vulkan için GLSL derleyicileri, eski lowp niteleyicisini mediump olarak yorumlar. Esnek hassasiyetle ilgili bazı örnekler:

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

GLSL'de açık kesinlik

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 bitlik kayan nokta, vektör ve matris türlerini bildirin:

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 skaler ve vektör türlerini bildirin:

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 duyarlık türünde bir anahtar kelime, minimum hassasiyeti belirtir ancak hedef donanım için daha yüksek hassasiyet daha iyiyse derleyici daha yüksek bir hassasiyetin yerine geçebilir. min16float anahtar kelimesi tarafından minimum bir hassasiyete sahip 16 bitlik hareketli değer belirtilir. Minimum hassasiyete sahip imzalı ve imzasız 16 bit tam sayılar, sırasıyla min16int ve min16uint anahtar kelimeleri tarafından belirtilir. Minimum hassasiyet bildirimlerinin diğer örnekleri şunlardır:

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

HLSL'de açık kesinlik

Yarı kesinli kayan nokta, half veya float16_t anahtar kelimeleri tarafından belirtilir. İmzalı ve imzasız 16 bit tam sayılar, sırasıyla int16_t ve uint16_t anahtar kelimeleriyle belirtilir. Açık hassasiyet bildirimlerinin diğer örnekleri şunlardır:

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