Ottimizza con precisione ridotta

Il formato numerico dei dati grafici e dei calcoli dello shabby può avere un impatto significativo sulle prestazioni del tuo gioco.

I formati ottimali:

  • Aumenta l'efficienza dell'utilizzo della cache della GPU
  • Riduzione del consumo della larghezza di banda della memoria, risparmio energetico e aumento delle prestazioni
  • Massimizza la velocità effettiva di calcolo nei programmi Shader
  • Riduci al minimo l'utilizzo della RAM della CPU del gioco

Formati con virgola mobile

La maggior parte dei calcoli e dei dati nella moderna grafica 3D utilizza numeri in virgola mobile. Vulkan su Android utilizza numeri in virgola mobile di 32 o 16 bit. Un numero in virgola mobile a 32 bit viene comunemente chiamato precisione singola o precisione totale e un numero in virgola mobile a 16 bit, mezza precisione.

Vulkan definisce un tipo in virgola mobile a 64 bit, ma questo tipo non è comunemente supportato dai dispositivi Vulkan su Android e il suo utilizzo non è consigliato. Un numero in virgola mobile a 64 bit viene comunemente chiamato a precisione doppia.

Formati numeri interi

I numeri interi firmati e non firmati vengono utilizzati anche per dati e calcoli. La dimensione intera standard è a 32 bit. Il supporto per altre dimensioni in bit dipende dal dispositivo. I dispositivi Vulkan con Android di solito supportano numeri interi a 16 e 8 bit. Vulkan definisce un tipo intero a 64 bit, ma questo tipo non è comunemente supportato dai dispositivi Vulkan su Android e il suo utilizzo non è consigliato.

Comportamento di metà precisione non ottimale

Le architetture GPU moderne combinano due valori a 16 bit in una coppia a 32 bit e implementano istruzioni che operano sulla coppia. Per prestazioni ottimali, non utilizzare variabili fluttuanti scalari a 16 bit e vettoria i dati in vettori a due o quattro elementi. Il compilatore shabbyr potrebbe essere in grado di utilizzare valori scalari nelle operazioni vettoriali. Tuttavia, se ti affidi al compilatore per ottimizzare i valori scalari, esamina l'output del compilatore per verificare la vettorializzazione.

La conversione da e verso la virgola mobile con precisione a 32 bit e 16 bit ha un costo di calcolo. Riduci l'overhead riducendo al minimo le conversioni di precisione nel tuo codice.

Confronta le differenze di rendimento tra le versioni a 16 e a 32 bit dei tuoi algoritmi. La metà della precisione non porta sempre a un miglioramento delle prestazioni, soprattutto per i calcoli complicati. Gli algoritmi che fanno un uso intensivo delle istruzioni fuse di aggiunta moltiplicazione (FMA) sui dati vettoriali sono buoni candidati per prestazioni migliori con precisione dimezzata.

Supporto del formato numerico

Tutti i dispositivi Vulkan su Android supportano numeri in virgola mobile a precisione singola a 32 bit e numeri interi a 32 bit nei calcoli di dati e shabby. Non è garantito che il supporto di altri formati sia disponibile e, se disponibile, non è garantito per tutti i casi d'uso.

Vulkan supporta due categorie di formati numerici facoltativi: aritmetica e archiviazione. Prima di utilizzare un formato specifico, assicurati che un dispositivo lo supporti in entrambe le categorie.

Supporto aritmetico

Un dispositivo Vulkan deve dichiarare il supporto aritmetico di un formato numerico affinché sia utilizzabile nei programmi shabbyr. I dispositivi Vulkan su Android in genere supportano i seguenti formati per l'aritmetica:

  • Numero intero a 32 bit (obbligatorio)
  • Virgola mobile a 32 bit (obbligatorio)
  • Numero intero a 8 bit (facoltativo)
  • Numero intero a 16 bit (facoltativo)
  • Virgola in virgola mobile a mezza precisione a 16 bit (facoltativo)

Per determinare se un dispositivo Vulkan supporta numeri interi a 16 bit per l'aritmetica, recupera le funzionalità del dispositivo chiamando la funzione vkGetPhysicalDeviceFeatures2() e controllando se il campo shaderInt16 nella struttura dei risultati di VkPhysicalDeviceFeatures2 è vera.

Per determinare se un dispositivo Vulkan supporta numeri in virgola mobile a 16 bit o numeri interi a 8 bit, segui questi passaggi:

  1. Controlla se il dispositivo supporta l'estensione Vulkan VK_KHR_shader_float16_int8. L'estensione è obbligatoria per il supporto di numeri interi a 16 bit e numeri in virgola mobile.
  2. Se VK_KHR_shader_float16_int8 è supportato, aggiungi un puntatore della struttura VkPhysicalDeviceShaderFloat16Int8Features a una catena VkPhysicalDeviceFeatures2.pNext.
  3. Controlla i campi shaderFloat16 e shaderInt8 della struttura dei risultati VkPhysicalDeviceShaderFloat16Int8Features dopo aver chiamato vkGetPhysicalDeviceFeatures2(). Se il valore del campo è true, il formato è supportato per l'aritmetica del programma shabbyr.

Sebbene non sia un requisito in Vulkan 1.1 o nel profilo Android Baseline del 2022, il supporto per l'estensione VK_KHR_shader_float16_int8 è molto comune sui dispositivi Android.

Assistenza per lo spazio di archiviazione

Un dispositivo Vulkan deve dichiarare il supporto di un formato numerico facoltativo per tipi di archiviazione specifici. L'estensione VK_KHR_16bit_storage dichiara il supporto per i formati di numeri interi a 16 bit e in virgola mobile a 16 bit. L'estensione definisce quattro tipi di archiviazione. Un dispositivo può supportare numeri a 16 bit per nessuno, alcuni o tutti i tipi di archiviazione.

I tipi di archiviazione sono:

  • Oggetti buffer di archiviazione
  • Oggetti buffer uniformi
  • Spingi blocchi costanti
  • Interfacce di input e di output dello Shader

La maggior parte dei dispositivi Vulkan 1.1 su Android supporta i formati a 16 bit negli oggetti buffer di archiviazione. Non prevedere il supporto basato sul modello GPU. I dispositivi con driver meno recenti per una determinata GPU potrebbero non supportare gli oggetti buffer di archiviazione, a differenza dei dispositivi con driver più recenti.

Il supporto per formati a 16 bit in buffer uniformi, blocchi costanti push e interfacce di input/output dello shadowr dipende generalmente dal produttore della GPU. Su Android, una GPU in genere supporta tutti e tre questi tipi o nessuno.

Una funzione di esempio che verifica il supporto dell'aritmetica e del formato di archiviazione 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;
  }
}

Livello di precisione per i dati

Un numero in virgola mobile a precisione dimezzata può rappresentare un intervallo di valori più piccolo con una precisione inferiore rispetto a un numero in virgola mobile a precisione singola. La metà precisione è spesso una scelta semplice e percettiva senza perdita rispetto alla precisione singola. Tuttavia, la precisione dimezza potrebbe non essere pratica in tutti i casi d'uso. Per alcuni tipi di dati, l'intervallo e la precisione ridotti possono causare artefatti grafici o un rendering errato.

I tipi di dati che sono buoni candidati per la rappresentazione in virgola mobile a precisione dimezzata includono:

  • Posizione dei dati nelle coordinate dello spazio locale
  • UV texture per texture più piccole con wrapping UV limitato che possono essere limitati a un intervallo di coordinate da -1,0 a 1,0
  • Dati normali, tangenti e bitangenti
  • Dati sui colori dei vertici
  • Dati con requisiti di precisione bassi centrati su 0,0

I tipi di dati che non sono consigliati per la rappresentazione in valori in virgola mobile a precisione media includono:

  • Posizione dei dati nelle coordinate globali
  • Texture UV per casi d'uso ad alta precisione, come le coordinate di un elemento UI in un foglio atlas

Precisione nel codice ombreggiato

I linguaggi di programmazione GLSL (OpenGL Shading Language) e High-level Shader Language (HLSL) supportano la specifica di precisione rilassata o precisione esplicita per i tipi numerici. La precisione rilassata è considerata un consiglio per il compilatore shabbyer. La precisione esplicita è un requisito della precisione specificata. I dispositivi Vulkan su Android in genere usano i formati a 16 bit, se suggeriti da una precisione rilassata. Altri dispositivi Vulkan, in particolare sui computer che utilizzano hardware grafico privo di supporto per i formati a 16 bit, potrebbero ignorare la precisione informale e utilizzare comunque i formati a 32 bit.

Estensioni di archiviazione in GLSL

È necessario definire le estensioni GLSL appropriate per abilitare il supporto di formati numerici a 16 o 8 bit nello spazio di archiviazione e in strutture di buffer uniformi. Le dichiarazioni delle estensioni pertinenti sono:

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

Queste estensioni sono specifiche per GLSL e non hanno un equivalente in HLSL.

Precisione rilassata in GLSL

Utilizza il qualificatore highp prima di un tipo in virgola mobile per suggerire un float a precisione singola e il qualificatore mediump per un float a precisione metà. I compilatori GLSL per Vulkan interpretano il qualificatore lowp precedente come mediump. Alcuni esempi di precisione rilassata:

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

Precisione esplicita in GLSL

Includi l'estensione GL_EXT_shader_explicit_arithmetic_types_float16 nel codice GLSL per abilitare l'uso di tipi in virgola mobile a 16 bit:

#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require

Dichiara i tipi scalari, vettoriali e matrici in virgola mobile a 16 bit in GLSL utilizzando le seguenti parole chiave:

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

Dichiara i tipi interi scalari e vettoriali a 16 bit in GLSL utilizzando le seguenti parole chiave:

int16_t     i16vec2     i16vec3    i16vec4
uint16_t    u16vec2     u16vec3    u16vec4

Precisione rilassata in HLSL

HLSL utilizza il termine precisione minima invece di precisione rilassata. Una parola chiave di tipo con precisione minima specifica la precisione minima, ma il compilatore potrebbe sostituire una precisione maggiore se una precisione più elevata è una scelta migliore per l'hardware di destinazione. La parola chiave min16float indica un valore in virgola mobile a 16 bit con precisione minima. I numeri interi a 16 bit firmati e non a precisione minima sono specificati rispettivamente dalle parole chiave min16int e min16uint. Ulteriori esempi di dichiarazioni di precisione minima includono quanto segue:

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

Precisione esplicita in HLSL

La precisione a virgola mobile metà è specificata dalle parole chiave half o float16_t. I numeri interi a 16 bit firmati e non firmati vengono specificati rispettivamente dalle parole chiave int16_t e uint16_t. Altri esempi di dichiarazioni di precisione esplicite includono quanto segue:

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