Mit geringerer Präzision optimieren

Das numerische Format von Grafikdaten und Shader-Berechnungen kann auf die Leistung deines Spiels auswirken.

Optimale Formate haben folgende Eigenschaften:

  • Effizienz der GPU-Cache-Nutzung erhöhen
  • Reduzieren Sie den Verbrauch der Arbeitsspeicherbandbreite, sparen Sie Strom und erhöhen die Leistung
  • Rechendurchsatz in Shader-Programmen maximieren
  • CPU-RAM-Nutzung deines Spiels minimieren

Gleitkommaformate

Die meisten Berechnungen und Daten in modernen 3D-Grafiken verwenden Gleitkommazahlen Zahlen. Vulkan auf Android verwendet Gleitkommazahlen, einer Größe von 16 Bit. Eine 32-Bit-Gleitkommazahl wird mit einfacher oder voller Precision; eine 16-Bit-Gleitkommazahl, Genauigkeit.

Vulkan definiert einen 64-Bit-Gleitkommatyp, dieser Typ ist jedoch im Allgemeinen nicht üblich die von Vulkan-Geräten unter Android unterstützt werden, und ihre Verwendung wird nicht empfohlen. 64-Bit Gleitkommazahl wird allgemein als doppelte Genauigkeit bezeichnet.

Ganzzahlformate

Vorzeichenbehaftete und vorzeichenlose Ganzzahlen werden auch für Daten und Berechnungen verwendet. Die Die Standard-Ganzzahlgröße beträgt 32 Bit. Andere Bitgrößen werden vom Gerät unterstützt. abhängig. Vulkan-Geräte mit Android unterstützen üblicherweise 16-Bit und 8-Bit Ganzzahlen. Vulkan definiert einen 64-Bit-Ganzzahltyp, der Typ ist aber normalerweise nicht üblich die von Vulkan-Geräten unter Android unterstützt werden, und ihre Verwendung wird nicht empfohlen.

Suboptimales Verhalten bei halber Genauigkeit

Moderne GPU-Architekturen kombinieren zwei 16-Bit-Werte in einem 32-Bit-Paar implementieren Sie Anweisungen für das Paar. Für eine optimale Leistung sollten Sie mit skalaren 16-Bit-Gleitkommavariablen; Daten in zwei oder vier Elemente vektorisieren Vektoren. Der Shader-Compiler kann möglicherweise skalare Werte in Vektoren verwenden Geschäftsabläufe. Wenn Sie jedoch auf den Compiler angewiesen sind, um Skalare zu optimieren, prüfen Sie die Compilerausgabe zur Überprüfung der Vektorisierung.

Die Konvertierung in und von 32-Bit- und 16-Bit-Gleitkommazahlen hat einen Berechnungskosten. Reduzieren Sie den Aufwand, indem Sie die Genauigkeit der Conversions in Ihrem Code.

Benchmark-Leistungsunterschiede zwischen 16-Bit- und 32-Bit-Versionen Ihrer Algorithmen. Eine halbe Genauigkeit führt nicht immer zu einer Leistungsverbesserung, vor allem bei komplizierten Berechnungen. Algorithmen, die kombinierte Daten in großem Umfang einsetzen Multiplikations-Add-Anweisungen (FMA) für vektorisierte Daten sind gute Kandidaten für bei halber Genauigkeit verbessert.

Unterstützung numerischer Formate

Alle Vulkan-Geräte auf Android unterstützen 32-Bit-Gleitkommazahlen mit einfacher Genauigkeit und 32-Bit-Ganzzahlen in Daten- und Shader-Berechnungen. Unterstützung für Die Verfügbarkeit anderer Formate kann nicht garantiert werden. Falls verfügbar, können wir auch nicht garantieren, für alle Anwendungsfälle.

In Vulkan werden optionale numerische Formate in zwei Kategorien unterstützt: Arithmetik und Speicherplatz. Bevor Sie ein bestimmtes Format verwenden, vergewissern Sie sich, dass ein Gerät es in beiden Kategorien.

Arithmetische Unterstützung

Ein Vulkan-Gerät muss arithmetische Unterstützung für ein numerisches Format deklarieren, damit es in Shader-Programmen brauchbar sein. Vulkan-Geräte unter Android unterstützen in der Regel die folgende Formate für Arithmetik verwenden:

  • 32-Bit-Ganzzahl (erforderlich)
  • 32-Bit-Gleitkomma (erforderlich)
  • 8-Bit-Ganzzahl (optional)
  • 16-Bit-Ganzzahl (optional)
  • 16-Bit-Gleitkommazahl mit halber Genauigkeit (optional)

So stellen Sie fest, ob ein Vulkan-Gerät 16-Bit-Ganzzahlen für die Arithmetik unterstützt: um die Funktionen des Geräts abzurufen, indem Sie die Methode vkGetPhysicalDeviceFeatures2() zur Verfügung. Dabei wird geprüft, das Feld shaderInt16 in VkPhysicalDeviceFeatures2 Ergebnisstruktur zutrifft.

So ermitteln Sie, ob ein Vulkan-Gerät 16-Bit-Gleitkommazahlen oder 8-Bit-Ganzzahlen unterstützt: führen Sie die folgenden Schritte aus:

  1. Prüfen Sie, ob das Gerät die VK_KHR_shader_float16_int8. Die Erweiterung ist erforderlich für die Unterstützung von 16-Bit-Gleitkommazahlen und 8-Bit-Ganzzahlen.
  2. Wenn VK_KHR_shader_float16_int8 unterstützt wird, hängen Sie einen Strukturzeiger VkPhysicalDeviceShaderFloat16Int8Features mit einer VkPhysicalDeviceFeatures2.pNext-Kette.
  3. Prüfen Sie die Felder shaderFloat16 und shaderInt8 der VkPhysicalDeviceShaderFloat16Int8Features-Ergebnisstruktur nach Aufruf vkGetPhysicalDeviceFeatures2(). Wenn der Feldwert true ist, lautet das Format: wird für die Arithmetik des Shader-Programms unterstützt.

Obwohl keine Anforderung in Vulkan 1.1 oder im Jahr 2022 Android Baseline-Profil: Unterstützung für VK_KHR_shader_float16_int8 ist auf Android-Geräten sehr üblich.

Speicherunterstützung

Ein Vulkan-Gerät muss die Unterstützung eines optionalen numerischen Formats für bestimmte Speichertypen. Erweiterung VK_KHR_16bit_storage deklariert die Unterstützung von 16-Bit-Ganzzahl- und 16-Bit-Gleitkommaformaten. Vier Speichertypen werden durch die Erweiterung definiert. Ein Gerät kann 16-Bit-Zahlen unterstützen für keinen, einige oder alle Speichertypen.

Es gibt folgende Speichertypen:

  • Zwischenspeicherobjekte
  • Einheitliche Pufferobjekte
  • Konstante Blöcke drücken
  • Shader-Eingabe- und -Ausgabeschnittstellen

Die meisten Vulkan 1.1-Geräte unter Android unterstützen 16-Bit-Formate in Storage-Pufferobjekte. Gehen Sie nicht von der Unterstützung für das GPU-Modell aus. Geräte mit älteren Treibern für eine bestimmte GPU. Geräte mit neueren Treibern hingegen schon.

Unterstützung von 16-Bit-Formaten in einheitlichen Zwischenspeichern, Push-Konstantenblöcken und Shader Eingabe-/Ausgabeschnittstellen sind in der Regel vom GPU-Hersteller abhängig. An Android unterstützt eine GPU in der Regel entweder alle drei dieser Typen oder keinen der .

Eine Beispielfunktion, die auf die Unterstützung von Vulkan-Arithmetik und Speicherformaten prüft:

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

Genauigkeit für Daten

Eine Gleitkommazahl mit halber Genauigkeit kann einen kleineren Wertebereich darstellen Gleitkommazahl mit geringerer Genauigkeit als eine Gleitkommazahl mit einfacher Genauigkeit. Halbe Genauigkeit ist oft eine einfache und spürbar verlustfreie Entscheidung gegenüber mit einfacher Genauigkeit. Allerdings ist eine halbe Genauigkeit nicht in allen Anwendungsfällen praktikabel. Bei einigen Datentypen können die reduzierte Reichweite und die Genauigkeit zu grafischen Darstellungen führen. Artefakte oder ein falsches Rendering.

Datentypen, die sich gut für eine Darstellung mit halber Genauigkeit eignen Gleitkommazahlen umfassen:

  • Positionsdaten in lokalen Raumkoordinaten
  • Texturen UVs für kleinere Texturen mit begrenzter UV-Einbindung, die auf einen Koordinatenbereich von -1,0 bis 1,0 beschränkt
  • Normale, Tangente und Bitangente
  • Vertex-Farbdaten
  • Daten mit geringen Anforderungen an die Genauigkeit, zentriert auf 0,0

Datentypen, die für die Darstellung als Gleitkommazahl mit halber Genauigkeit nicht empfohlen werden umfassen:

  • Positionsdaten in globalen Weltkoordinaten
  • UV-Texturen für hochpräzise Anwendungsfälle wie UI-Elementkoordinaten in einem Atlas-Blatt

Genauigkeit im Shader-Code

Die OpenGL Shading Language (GLSL) und HLSL-Shader (High-Level Shader Language) Programmiersprachen unterstützen die Spezifikation von entspannten oder explizite Genauigkeit für numerische Typen. Entspannte Präzision als Empfehlung für den Shader-Compiler. Explizite Genauigkeit ist ein Anforderung der angegebenen Genauigkeit. Vulkan-Geräte mit Android nutzen in der Regel 16-Bit-Formate, wenn diese von geringer Genauigkeit vorgeschlagen werden. Andere Vulkan-Geräte insbesondere auf Desktop-Computern, deren Grafikhardware nicht unterstützt wird Bei 16-Bit-Formaten wird die gelockerte Genauigkeit möglicherweise ignoriert und trotzdem 32-Bit-Formate verwendet.

Speichererweiterungen in GLSL

Die entsprechenden GLSL-Erweiterungen müssen definiert werden, damit 16-Bit- oder Numerische 8-Bit-Formate in Speicher- und einheitlichen Pufferstrukturen Die relevanten Erweiterungsdeklarationen sind:

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

Diese Erweiterungen sind GLSL-spezifisch und haben kein Äquivalent in HLSL.

Text "Relaxed Precision" in GLSL

Verwenden Sie den Qualifizierer highp vor einem Gleitkommatyp, um einen Vorschlag Gleitkommazahl mit einfacher Genauigkeit und der Qualifizierer mediump für eine Gleitkommazahl mit halber Genauigkeit. GLSL-Compiler für Vulkan interpretieren den Legacy-Qualifier lowp als mediump. Einige Beispiele für gelockerte Genauigkeit:

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

Explizite Genauigkeit in GLSL

Fügen Sie die Erweiterung GL_EXT_shader_explicit_arithmetic_types_float16 in Ihr GLSL-Code zur Verwendung von 16-Bit-Gleitkommatypen:

#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require

16-Bit-Gleitkommaskalar, -Vektor und -Matrixtypen in GLSL mit der folgenden Keywords:

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

Geben Sie 16-Bit-Ganzzahlskalartypen und Vektortypen in GLSL mit folgendem Befehl an: Keywords:

int16_t     i16vec2     i16vec3    i16vec4
uint16_t    u16vec2     u16vec3    u16vec4

Entspannte Präzision in HLSL

HLSL verwendet den Begriff minimale Precision (minimale Genauigkeit) anstelle von lockerer Genauigkeit. Ein Minimum Precision-Typ gibt die minimale Genauigkeit an, aber der Compiler kann können Sie eine höhere Genauigkeit durch eine höhere Genauigkeit ersetzen, wenn diese die bessere Wahl für das Zielhardware. Eine 16-Bit-Gleitkommazahl mit minimaler Genauigkeit wird durch den min16float Keyword. Vorzeichenbehaftete und vorzeichenlose 16-Bit-Ganzzahlen mit minimaler Genauigkeit sind min16int bzw. min16uint angegeben wird. Zusätzliche Informationen Beispiele für Deklarationen mit minimaler Genauigkeit sind:

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

Explizite Genauigkeit in HLSL

Ein Gleitkommawert mit halber Genauigkeit wird durch half oder float16_t angegeben Keywords. Vorzeichenbehaftete und vorzeichenlose 16-Bit-Ganzzahlen werden in der int16_t angegeben bzw. uint16_t Keywords. Weitere Beispiele für explizite Precision -Deklarationen enthalten:

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