Start · Sprachen · JavaScript · Referenz · WebGLShaderPrecisionFormat.rangeMax

WebGLShaderPrecisionFormat.rangeMax

Eigenschaft

Schreibgeschützte Ganzzahl: Basis-2-Logarithmus des Betrags des größten Werts, den ein Shader-Datentyp in der abgefragten Genauigkeit darstellen kann.

seit JavaScript WebGL 1.0 – Baseline: alle modernen Browser Kategorie: webapi

Signatur

WebGLShaderPrecisionFormat.rangeMax

Beschreibung

Die schreibgeschützte Eigenschaft rangeMax eines WebGLShaderPrecisionFormat-Objekts gibt an, wie groß Werte in einem Shader-Typ höchstens werden können. Der Wert ist der Exponent zur Basis 2: Bei rangeMax = 127 reicht der darstellbare Bereich also bis etwa 2^127. Ein solches Objekt bekommt man über gl.getShaderPrecisionFormat(shaderType, precisionType).

Zusammen mit rangeMin (Exponent für den kleinsten darstellbaren Wert bzw. den negativen Bereich) und precision (Anzahl der Mantissenbits) beschreibt rangeMax, welche numerischen Eigenschaften lowp, mediump und highp auf der jeweiligen GPU tatsächlich haben. Diese Angaben unterscheiden sich je nach Gerät, besonders bei mobilen GPUs.

In der Praxis prüft man damit vor dem Kompilieren eines Shaders, ob z. B. highp float im Fragment-Shader unterstützt wird. Ist das nicht der Fall, sind alle drei Werte 0. Außerdem lässt sich abschätzen, ob Berechnungen mit großen Zahlen (Weltkoordinaten, Zeitwerte, Tiefenwerte) in mediump überlaufen würden.

Typische Werte: Bei IEEE-754-Floats mit einfacher Genauigkeit (highp float) ist rangeMax gleich 127, bei mediump float auf vielen Mobil-GPUs 15 (Half-Float, Maximum etwa 65504). Bei Ganzzahltypen (HIGH_INT usw.) liegt der Wert oft bei 31, bei 24 oder niedriger.

Rückgabewert

Typ
number
Beschreibung
Ganzzahl (GLint): Basis-2-Logarithmus des Betrags des größten darstellbaren Werts. 0, wenn die Genauigkeitsstufe nicht unterstützt wird.

Beispiele

rangeMax für highp float im Fragment-Shader auslesen

const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');

const fmt = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
console.log('rangeMin:', fmt.rangeMin);
console.log('rangeMax:', fmt.rangeMax);
console.log('precision:', fmt.precision);
console.log('Maximalwert ≈', 2 ** fmt.rangeMax);
rangeMin: 127 rangeMax: 127 precision: 23 Maximalwert ≈ 1.7014118346046923e+38

Passende Genauigkeit für den Shader wählen

const gl = document.createElement('canvas').getContext('webgl');

const supportsHighp = () => {
  const { rangeMax, precision } =
    gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT);
  return rangeMax > 0 && precision > 0;
};

const precisionLine = supportsHighp()
  ? 'precision highp float;'
  : 'precision mediump float;';

const fragmentSource = `${precisionLine}
void main() {
  gl_FragColor = vec4(1.0, 0.5, 0.0, 1.0);
}`;
console.log(precisionLine);
precision highp float;

Überlauf-Risiko für große Werte prüfen

const gl = document.createElement('canvas').getContext('webgl');

// Passt ein Wert (z. B. vergangene Millisekunden) in mediump?
const fitsInPrecision = (value, shaderType, precisionType) => {
  const { rangeMax } = gl.getShaderPrecisionFormat(shaderType, precisionType);
  return Math.abs(value) < 2 ** rangeMax;
};

const elapsedMs = 120000;
console.log(
  'mediump ok?',
  fitsInPrecision(elapsedMs, gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT)
);
mediump ok? false // auf Geräten mit rangeMax 15; auf Desktop-GPUs oft true

Alle Formate tabellarisch ausgeben

const gl = document.createElement('canvas').getContext('webgl');
const shaders = { VERTEX: gl.VERTEX_SHADER, FRAGMENT: gl.FRAGMENT_SHADER };
const types = ['LOW_FLOAT', 'MEDIUM_FLOAT', 'HIGH_FLOAT', 'LOW_INT', 'MEDIUM_INT', 'HIGH_INT'];

const rows = [];
for (const [sName, s] of Object.entries(shaders)) {
  for (const t of types) {
    const f = gl.getShaderPrecisionFormat(s, gl[t]);
    rows.push({ shader: sName, type: t, rangeMin: f.rangeMin, rangeMax: f.rangeMax, precision: f.precision });
  }
}
console.table(rows);
(Tabelle mit 12 Zeilen, Werte abhängig von der GPU)

// Wichtig · Fallstricke

  • Nur ein Exponent: rangeMax ist der Exponent und nicht der Maximalwert selbst. Den ungefähren Grenzwert erhält man mit 2 ** rangeMax.
  • Kein Unterstützungs-Flag: Wird eine Genauigkeitsstufe nicht unterstützt (klassisch HIGH_FLOAT im Fragment-Shader älterer Mobil-GPUs), sind rangeMin, rangeMax und precision alle 0.
  • Desktop meldet oft überall highp: Viele Desktop-Treiber und ANGLE geben für lowp und mediump dieselben Werte wie für highp zurück (127). Tests auf dem Desktop sagen deshalb nichts über das Verhalten auf Mobilgeräten aus.
  • Ganzzahltypen: Bei *_INT beschreibt rangeMax den Ganzzahlbereich, und precision ist dann 0.
  • Fingerprinting: Die Werte sind geräteabhängig und werden zum Browser-Fingerprinting genutzt. Datenschutz-Modi (z. B. Tor Browser, Firefox mit resistFingerprinting) können vereinheitlichte Werte liefern.
  • Verlorener Kontext: Ist der WebGL-Kontext verloren, gibt getShaderPrecisionFormat() null zurück. Vor dem Zugriff auf rangeMax also auf null prüfen.
  • Verfügbar in WebGL 1 und WebGL 2, in allen modernen Browsern unterstützt.