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 in der Regel 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:
- 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.
- Wenn
VK_KHR_shader_float16_int8
unterstützt wird, hängen Sie einen Strukturzeiger VkPhysicalDeviceShaderFloat16Int8Features mit einerVkPhysicalDeviceFeatures2.pNext
-Kette. - Prüfen Sie die Felder
shaderFloat16
undshaderInt8
derVkPhysicalDeviceShaderFloat16Int8Features
-Ergebnisstruktur nach AufrufvkGetPhysicalDeviceFeatures2()
. Wenn der Feldwerttrue
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 Puffern, konstanten Push-Blö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 Genauigkeit
-Deklarationen enthalten:
// Four element vector and four-by-four matrix types
half4 my_vector4;
half4x4 my_matrix4x4;